Importing rustc-1.40.0 Bug: 146571186 Change-Id: I4bea4097df75c2f63b0e74758c75b40f5922ecc4
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4daaa98..37a217d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md
@@ -15,7 +15,7 @@ * [Helpful Links and Information](#helpful-links-and-information) If you have questions, please make a post on [internals.rust-lang.org][internals] or -hop on the [Rust Discord server][rust-discord], [Rust Zulip server][rust-zulip] or [#rust-internals][pound-rust-internals]. +hop on the [Rust Discord server][rust-discord] or [Rust Zulip server][rust-zulip]. As a reminder, all contributors are expected to follow our [Code of Conduct][coc]. @@ -25,7 +25,6 @@ If this is your first time contributing, the [walkthrough] chapter of the guide can give you a good example of how a typical contribution would go. -[pound-rust-internals]: https://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust-internals [internals]: https://internals.rust-lang.org [rust-discord]: http://discord.gg/rust-lang [rust-zulip]: https://rust-lang.zulipchat.com @@ -129,6 +128,14 @@ Also, please make sure that fixup commits are squashed into other related commits with meaningful commit messages. +GitHub allows [closing issues using keywords][closing-keywords]. This feature +should be used to keep the issue tracker tidy. However, it is generally preferred +to put the "closes #123" text in the PR description rather than the issue commit; +particularly during rebasing, citing the issue number in the commit can "spam" +the issue in question. + +[closing-keywords]: https://help.github.com/en/articles/closing-issues-using-keywords + Please make sure your pull request is in compliance with Rust's style guidelines by running @@ -404,7 +411,7 @@ There are a number of other ways to contribute to Rust that don't deal with this repository. -Answer questions in [#rust][pound-rust], or on [users.rust-lang.org][users], +Answer questions in the _Get Help!_ channels from the [Rust Discord server][rust-discord], on [users.rust-lang.org][users], or on [StackOverflow][so]. Participate in the [RFC process](https://github.com/rust-lang/rfcs). @@ -413,7 +420,7 @@ it to [Crates.io](http://crates.io). Easier said than done, but very, very valuable! -[pound-rust]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust +[rust-discord]: https://discord.gg/rust-lang [users]: https://users.rust-lang.org/ [so]: http://stackoverflow.com/questions/tagged/rust [community-library]: https://github.com/rust-lang/rfcs/labels/A-community-library
diff --git a/Cargo.lock b/Cargo.lock index 5bc1938..f44ed3d 100644 --- a/Cargo.lock +++ b/Cargo.lock
@@ -27,16 +27,16 @@ [[package]] name = "ammonia" -version = "2.1.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384d704f242a0a9faf793fff775a0be6ab9aa27edabffa097331d73779142520" +checksum = "9e266e1f4be5ffa05309f650e2586fe1d3ae6034eb24025a7ae1dfecc330823a" dependencies = [ "html5ever", "lazy_static 1.3.0", "maplit", "matches", "tendril", - "url 1.7.2", + "url 2.1.0", ] [[package]] @@ -108,10 +108,16 @@ ] [[package]] -name = "backtrace" -version = "0.3.37" +name = "autocfg" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5180c5a20655b14a819b652fd2378fa5f1697b6c9ddad3e695c2f9cedf6df4e2" +checksum = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" + +[[package]] +name = "backtrace" +version = "0.3.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" dependencies = [ "backtrace-sys", "cfg-if", @@ -123,9 +129,9 @@ [[package]] name = "backtrace-sys" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3a000b9c543553af61bc01cbfc403b04b5caa9e421033866f2e98061eb3e61" +checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" dependencies = [ "cc", "compiler_builtins", @@ -134,19 +140,10 @@ ] [[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] - -[[package]] name = "bitflags" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "blake2-rfc" @@ -243,7 +240,6 @@ checksum = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" dependencies = [ "byteorder", - "either", "iovec", ] @@ -265,10 +261,11 @@ [[package]] name = "cargo" -version = "0.40.0" +version = "0.41.0" dependencies = [ "atty", "bytesize", + "cargo-platform", "cargo-test-macro", "cargo-test-support", "clap", @@ -278,7 +275,7 @@ "crypto-hash", "curl", "curl-sys", - "env_logger", + "env_logger 0.7.1", "failure", "filetime", "flate2", @@ -326,6 +323,13 @@ ] [[package]] +name = "cargo-platform" +version = "0.1.0" +dependencies = [ + "serde", +] + +[[package]] name = "cargo-test-macro" version = "0.1.0" @@ -360,14 +364,26 @@ ] [[package]] +name = "cargo_metadata" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2d1617e838936c0d2323a65cc151e03ae19a7678dd24f72bccf27119b90a5d" +dependencies = [ + "semver", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] name = "cargotest2" version = "0.1.0" [[package]] name = "cc" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5f3fee5eeb60324c2781f1e41286bdee933850fff9b3c672587fed5ec58c83" +checksum = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" [[package]] name = "cfg-if" @@ -429,7 +445,7 @@ name = "clippy" version = "0.0.212" dependencies = [ - "cargo_metadata", + "cargo_metadata 0.9.0", "clippy-mini-macro-test", "clippy_lints", "compiletest_rs", @@ -450,12 +466,12 @@ name = "clippy_lints" version = "0.0.212" dependencies = [ - "cargo_metadata", + "cargo_metadata 0.9.0", "if_chain", "itertools 0.8.0", "lazy_static 1.3.0", "matches", - "pulldown-cmark 0.6.0", + "pulldown-cmark 0.6.1", "quine-mc_cluskey", "regex-syntax", "semver", @@ -526,7 +542,7 @@ version = "0.0.0" dependencies = [ "diff", - "env_logger", + "env_logger 0.7.1", "getopts", "lazy_static 1.3.0", "libc", @@ -542,9 +558,9 @@ [[package]] name = "compiletest_rs" -version = "0.3.22" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ecc9332b68270998995c00f8051ee856121764a0d3230e64c9efd059d27b6" +checksum = "f75b10a18fb53549fdd090846eb01c7f8593914494d1faabc4d3005c436e417a" dependencies = [ "diff", "filetime", @@ -558,7 +574,6 @@ "serde_derive", "serde_json", "tempfile", - "tester", "winapi 0.3.6", ] @@ -593,7 +608,7 @@ [[package]] name = "crates-io" -version = "0.28.0" +version = "0.29.0" dependencies = [ "curl", "failure", @@ -653,6 +668,16 @@ ] [[package]] +name = "crossbeam-deque" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" +dependencies = [ + "crossbeam-epoch 0.7.2", + "crossbeam-utils 0.6.5", +] + +[[package]] name = "crossbeam-epoch" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -723,25 +748,24 @@ [[package]] name = "curl" -version = "0.4.21" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85f2f95f2bd277d316d1aa8a477687ab4a6942258c7db7c89c187534669979c" +checksum = "d08ad3cb89d076a36b0ce5749eec2c9964f70c0c58480ab6b75a91ec4fc206d8" dependencies = [ "curl-sys", - "kernel32-sys", "libc", "openssl-probe", "openssl-sys", "schannel", "socket2", - "winapi 0.2.8", + "winapi 0.3.6", ] [[package]] name = "curl-sys" -version = "0.4.18" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d91a0052d5b982887d8e829bee0faffc7218ea3c6ebd3d6c2c8f678a93c9a42" +checksum = "2e9a9a4e417722876332136a00cacf92c2ceb331fab4b52b6a1ad16c6cd79255" dependencies = [ "cc", "libc", @@ -881,12 +905,6 @@ ] [[package]] -name = "dtoa" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" - -[[package]] name = "either" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -909,23 +927,14 @@ [[package]] name = "ena" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dc01d68e08ca384955a3aeba9217102ca1aa85b6e168639bf27739f1d749d87" +checksum = "8944dc8fa28ce4a38f778bd46bf7d923fe73eed5a439398507246c8e017e6f36" dependencies = [ "log", ] [[package]] -name = "encoding_rs" -version = "0.8.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4155785c79f2f6701f185eb2e6b4caf0555ec03477cb4c70db67b465311620ed" -dependencies = [ - "cfg-if", -] - -[[package]] name = "env_logger" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -939,6 +948,19 @@ ] [[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] name = "error-chain" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -974,7 +996,7 @@ "proc-macro2 0.4.30", "quote 0.6.12", "syn 0.15.35", - "synstructure", + "synstructure 0.10.2", ] [[package]] @@ -1116,16 +1138,6 @@ checksum = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869" [[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -dependencies = [ - "futures", - "num_cpus", -] - -[[package]] name = "fwdansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1217,30 +1229,12 @@ version = "0.0.0" [[package]] -name = "h2" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a539b63339fbbb00e081e84b6e11bd1d9634a82d91da2984a18ac74a8823f392" -dependencies = [ - "byteorder", - "bytes", - "fnv", - "futures", - "http", - "indexmap", - "log", - "slab", - "string", - "tokio-io", -] - -[[package]] name = "handlebars" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df044dd42cdb7e32f28557b661406fc0f2494be75199779998810dbc35030e0d" dependencies = [ - "hashbrown", + "hashbrown 0.5.0", "lazy_static 1.3.0", "log", "pest", @@ -1257,10 +1251,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353" dependencies = [ + "serde", +] + +[[package]] +name = "hashbrown" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd9867f119b19fecb08cd5c326ad4488d7a1da4bf75b4d95d71db742525aaab" +dependencies = [ + "autocfg", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", - "serde", ] [[package]] @@ -1273,6 +1276,17 @@ ] [[package]] +name = "hermit-abi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f22b8f315b98f415780ddbe9163c7dbbc5a07225b6d102ace1d8aeef85775140" +dependencies = [ + "compiler_builtins", + "libc", + "rustc-std-workspace-core", +] + +[[package]] name = "hex" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1286,9 +1300,9 @@ [[package]] name = "home" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07c315e106bd6f83f026a20ddaeef2706782e490db1dcdd37caad38a0e895b3" +checksum = "a3753954f7bd71f0e671afb8b5a992d1724cf43b7f95a563cd4a0bde94659ca8" dependencies = [ "scopeguard 1.0.0", "winapi 0.3.6", @@ -1296,100 +1310,28 @@ [[package]] name = "html5ever" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce65ac8028cf5a287a7dbf6c4e0a6cf2dcf022ed5b167a81bae66ebf599a8b7" +checksum = "025483b0a1e4577bb28578318c886ee5f817dda6eb62473269349044406644cb" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2 0.4.30", - "quote 0.6.12", - "syn 0.15.35", + "proc-macro2 1.0.3", + "quote 1.0.2", + "syn 1.0.5", ] [[package]] -name = "http" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe67e3678f2827030e89cc4b9e7ecd16d52f132c0b940ab5005f88e821500f6a" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" -dependencies = [ - "bytes", - "futures", - "http", - "tokio-buf", -] - -[[package]] -name = "httparse" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" - -[[package]] name = "humantime" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ "quick-error", ] [[package]] -name = "hyper" -version = "0.12.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6481fff8269772d4463253ca83c788104a7305cb3fb9136bc651a6211e46e03f" -dependencies = [ - "bytes", - "futures", - "futures-cpupool", - "h2", - "http", - "http-body", - "httparse", - "iovec", - "itoa", - "log", - "net2", - "rustc_version", - "time", - "tokio", - "tokio-buf", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-tcp", - "tokio-threadpool", - "tokio-timer", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" -dependencies = [ - "bytes", - "futures", - "hyper", - "native-tls", - "tokio-io", -] - -[[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1564,9 +1506,9 @@ [[package]] name = "jsonrpc-core" -version = "13.1.0" +version = "13.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd42951eb35079520ee29b7efbac654d85821b397ef88c8151600ef7e2d00217" +checksum = "91d767c183a7e58618a609499d359ce3820700b3ebb4823a18c343b4a2a41a0d" dependencies = [ "futures", "log", @@ -1636,7 +1578,7 @@ "num_cpus", "tokio", "tokio-codec", - "unicase 2.5.1", + "unicase", ] [[package]] @@ -1669,27 +1611,14 @@ [[package]] name = "libc" -version = "0.2.62" +version = "0.2.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" +checksum = "74dfca3d9957906e8d1e6a0b641dc9a59848e793f1da2165889fd4f62d10d79c" dependencies = [ "rustc-std-workspace-core", ] [[package]] -name = "libflate" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90c6f86f4b0caa347206f916f8b687b51d77c6ef8ff18d52dd007491fd580529" -dependencies = [ - "adler32", - "byteorder", - "crc32fast", - "rle-decode-fast", - "take_mut", -] - -[[package]] name = "libgit2-sys" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1835,9 +1764,9 @@ [[package]] name = "markup5ever" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1af46a727284117e09780d05038b1ce6fc9c76cc6df183c3dae5a8955a25e21" +checksum = "65381d9d47506b8592b97c4efd936afcf673b09b059f2bef39c7211ee78b9d03" dependencies = [ "log", "phf", @@ -1858,15 +1787,15 @@ [[package]] name = "mdbook" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949bb2acb2cff9fa5c375cf9c43e70b3dba0a974d9fe01c31285d7a84d2a0fa2" +checksum = "031bdd9d4893c983e2f69ebc4b59070feee8276a584c4aabdcb351235ea28016" dependencies = [ "ammonia", "chrono", "clap", "elasticlunr-rs", - "env_logger", + "env_logger 0.6.2", "error-chain", "handlebars", "itertools 0.8.0", @@ -1874,7 +1803,7 @@ "log", "memchr", "open", - "pulldown-cmark 0.5.3", + "pulldown-cmark 0.6.1", "regex", "serde", "serde_derive", @@ -1886,33 +1815,10 @@ ] [[package]] -name = "mdbook-linkcheck" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77d1f0ba4d1e6b86fa18e8853d026d7d76a97eb7eb5eb052ed80901e43b7fc10" -dependencies = [ - "env_logger", - "failure", - "log", - "mdbook", - "memchr", - "pulldown-cmark 0.5.3", - "rayon", - "regex", - "reqwest", - "semver", - "serde", - "serde_derive", - "serde_json", - "structopt 0.2.18", - "url 1.7.2", -] - -[[package]] name = "measureme" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09de7dafa3aa334bc806447c7e4de69419723312f4b88b80b561dea66601ce8" +checksum = "cd21b0e6e1af976b269ce062038fe5e1b9ca2f817ab7a3af09ec4210aebf0d30" dependencies = [ "byteorder", "memmap", @@ -1951,27 +1857,6 @@ ] [[package]] -name = "mime" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e27ca21f40a310bd06d9031785f4801710d566c184a6e15bad4f1d9b65f9425" -dependencies = [ - "unicase 2.5.1", -] - -[[package]] -name = "mime_guess" -version = "2.0.0-alpha.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" -dependencies = [ - "mime", - "phf", - "phf_codegen", - "unicase 1.4.2", -] - -[[package]] name = "minifier" version = "0.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2080,13 +1965,13 @@ version = "0.1.0" dependencies = [ "byteorder", - "cargo_metadata", + "cargo_metadata 0.9.0", "colored", "compiletest_rs", "directories", - "env_logger", + "env_logger 0.7.1", "getrandom", - "hex 0.3.2", + "hex 0.4.0", "log", "num-traits", "rand 0.7.0", @@ -2097,24 +1982,6 @@ ] [[package]] -name = "native-tls" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" -dependencies = [ - "lazy_static 1.3.0", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] name = "net2" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2444,7 +2311,6 @@ checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" dependencies = [ "siphasher", - "unicase 1.4.2", ] [[package]] @@ -2493,7 +2359,7 @@ checksum = "df8b3f4e0475def7d9c2e5de8e5a1306949849761e107b360d03e98eafaffd61" dependencies = [ "chrono", - "env_logger", + "env_logger 0.6.2", "log", ] @@ -2558,21 +2424,20 @@ checksum = "77043da1282374688ee212dc44b3f37ff929431de9c9adc3053bd3cee5630357" dependencies = [ "bitflags", - "getopts", "memchr", - "unicase 2.5.1", + "unicase", ] [[package]] name = "pulldown-cmark" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b0ad0d4c1702965ee6bb5b4ff5e71f83850b497d497e9444302987bf9e26a4" +checksum = "1c205cc82214f3594e2d50686730314f817c67ffa80fe800cf0db78c3c2b9d9e" dependencies = [ "bitflags", "getopts", "memchr", - "unicase 2.5.1", + "unicase", ] [[package]] @@ -2613,14 +2478,14 @@ [[package]] name = "racer" -version = "2.1.27" +version = "2.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde22b84ab75220015cbd91240222402bf885cbe3a5dc856475771abb82533ae" +checksum = "acc70369054bad4ad0c16a3f45cd73e0695361a3af35c7b465e619ac2674f064" dependencies = [ "bitflags", "clap", "derive_more", - "env_logger", + "env_logger 0.6.2", "humantime", "lazy_static 1.3.0", "log", @@ -2772,22 +2637,22 @@ [[package]] name = "rayon" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b0186e22767d5b9738a05eab7c6ac90b15db17e5b5f9bd87976dd7d89a10a4" +checksum = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" dependencies = [ - "crossbeam-deque 0.6.3", + "crossbeam-deque 0.7.1", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2" +checksum = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" dependencies = [ - "crossbeam-deque 0.6.3", + "crossbeam-deque 0.7.1", "crossbeam-queue", "crossbeam-utils 0.6.5", "lazy_static 1.3.0", @@ -2870,51 +2735,15 @@ ] [[package]] -name = "reqwest" -version = "0.9.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e542d9f077c126af32536b6aacc75bb7325400eab8cd0743543be5d91660780d" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures", - "http", - "hyper", - "hyper-tls", - "libflate", - "log", - "mime", - "mime_guess", - "native-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-executor", - "tokio-io", - "tokio-threadpool", - "tokio-timer", - "url 1.7.2", - "uuid", -] - -[[package]] -name = "rle-decode-fast" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" - -[[package]] name = "rls" -version = "1.39.0" +version = "1.40.0" dependencies = [ "cargo", - "cargo_metadata", + "cargo_metadata 0.8.0", "clippy_lints", "crossbeam-channel", "difference", - "env_logger", + "env_logger 0.7.1", "failure", "futures", "heck", @@ -2928,7 +2757,7 @@ "num_cpus", "ordslice", "racer", - "rand 0.6.1", + "rand 0.7.0", "rayon", "regex", "rls-analysis", @@ -2937,7 +2766,6 @@ "rls-rustc", "rls-span", "rls-vfs", - "rustc-serialize", "rustc-workspace-hack", "rustc_tools_util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustfmt-nightly", @@ -2998,11 +2826,11 @@ version = "0.6.0" dependencies = [ "clippy_lints", - "env_logger", + "env_logger 0.7.1", "failure", "futures", "log", - "rand 0.6.1", + "rand 0.7.0", "rls-data", "rls-ipc", "serde", @@ -3035,7 +2863,7 @@ "clap", "failure", "mdbook", - "mdbook-linkcheck", + "rustc-workspace-hack", ] [[package]] @@ -3046,6 +2874,7 @@ "backtrace", "bitflags", "byteorder", + "cc", "chalk-engine", "fmt_macros", "graphviz", @@ -3055,12 +2884,13 @@ "num_cpus", "parking_lot 0.9.0", "polonius-engine", - "rustc-rayon", - "rustc-rayon-core", + "rustc-rayon 0.3.0", + "rustc-rayon-core 0.3.0", "rustc_apfloat", "rustc_data_structures", "rustc_errors", "rustc_fs_util", + "rustc_index", "rustc_macros", "rustc_target", "scoped-tls", @@ -3072,9 +2902,9 @@ [[package]] name = "rustc-ap-arena" -version = "583.0.0" +version = "606.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f59b76d334bd533f3fdc5c651c27678c5e80fac67c6f7da22ba21a58878c55f5" +checksum = "a623fd4805842e9bd0bb6e6dace63efede0ee22de4522a0b03b7c3d15a22f009" dependencies = [ "rustc-ap-rustc_data_structures", "smallvec", @@ -3082,15 +2912,15 @@ [[package]] name = "rustc-ap-graphviz" -version = "583.0.0" +version = "606.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e632ef08ca17458acfd46d2ead3d541a1c249586cd5329f5fe333dacfab6142" +checksum = "ee549ade784b444ef10c0240c3487ed785aa65d711071f7984246b15329a17b6" [[package]] name = "rustc-ap-rustc_data_structures" -version = "583.0.0" +version = "606.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89e2c7be68185418f3cd56af3df8b29007a59a1cebefa63612d055f9bcb1a36" +checksum = "ca545744a5a9b42e3d0410d6290d40de96dd567253fe77f310c1de4afd213dd4" dependencies = [ "cfg-if", "crossbeam-utils 0.6.5", @@ -3099,21 +2929,22 @@ "jobserver", "lazy_static 1.3.0", "log", - "parking_lot 0.7.1", + "parking_lot 0.9.0", "rustc-ap-graphviz", + "rustc-ap-rustc_index", "rustc-ap-serialize", "rustc-hash", - "rustc-rayon", - "rustc-rayon-core", + "rustc-rayon 0.2.0", + "rustc-rayon-core 0.2.0", "smallvec", "stable_deref_trait", ] [[package]] name = "rustc-ap-rustc_errors" -version = "583.0.0" +version = "606.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e47cb380abeb72b01e42b2342d592f7eeea7d536c2f1f0d0e550dc509e46333" +checksum = "a6967a41ed38ef4bce0f559fe9a4801d8ba12ac032f40a12a55e72f79d52c9bb" dependencies = [ "annotate-snippets", "atty", @@ -3127,45 +2958,56 @@ ] [[package]] -name = "rustc-ap-rustc_lexer" -version = "583.0.0" +name = "rustc-ap-rustc_index" +version = "606.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494cfaf67f49217d67d0774eeecbba61ac89acf478db97ef11f113ed8a959305" +checksum = "457a5c204ae2fdaa5bdb5b196e58ca59896870d80445fe423063c9453496e3ea" +dependencies = [ + "rustc-ap-serialize", + "smallvec", +] + +[[package]] +name = "rustc-ap-rustc_lexer" +version = "606.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0c064676f8a08e42a36b0d4e4a102465fb0f4b75e11436cb7f66d2c3fa7139" dependencies = [ "unicode-xid 0.2.0", ] [[package]] name = "rustc-ap-rustc_macros" -version = "583.0.0" +version = "606.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e5d36becc59b4497f9cbd3ae0610081de0207a1d0e95c066369167b14f486f" +checksum = "b2d77e46159c5288c585decbcdc9d742889c65e307c31e104c7a36d63fe1f5d0" dependencies = [ "itertools 0.8.0", "proc-macro2 0.4.30", "quote 0.6.12", "syn 0.15.35", - "synstructure", + "synstructure 0.10.2", ] [[package]] name = "rustc-ap-rustc_target" -version = "583.0.0" +version = "606.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7bfc5f96dfc3b9f8d5b57884f7f37467ecff6776cd4b8b491a7daece6fdd7c2" +checksum = "86ca895350b0de14d064b499168c93fa183958d5462eb042c927d93623e41ec1" dependencies = [ "bitflags", "log", "rustc-ap-rustc_data_structures", + "rustc-ap-rustc_index", "rustc-ap-serialize", "rustc-ap-syntax_pos", ] [[package]] name = "rustc-ap-serialize" -version = "583.0.0" +version = "606.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb9ee231cf79eded39c56647499f83d6136ff5c8c0baaa9e21b6febee00f4f6" +checksum = "92679240e86f4583cc05f8dcf6439bdab87bac9e6555718469176de9bd52ba20" dependencies = [ "indexmap", "smallvec", @@ -3173,17 +3015,17 @@ [[package]] name = "rustc-ap-syntax" -version = "583.0.0" +version = "606.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3827fc208814efbde82d613e31d11b4250ce9e8cf8afe4a4d47bbbd099632c9" +checksum = "0a0c30f8e38c847dbfd9e2f1e472ab06d0bd0a23ab53ae4c5a44912842ce834e" dependencies = [ "bitflags", "lazy_static 1.3.0", "log", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", + "rustc-ap-rustc_index", "rustc-ap-rustc_lexer", - "rustc-ap-rustc_macros", "rustc-ap-rustc_target", "rustc-ap-serialize", "rustc-ap-syntax_pos", @@ -3193,13 +3035,14 @@ [[package]] name = "rustc-ap-syntax_pos" -version = "583.0.0" +version = "606.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930ed81c34f325e512cc315c04d676fa84a373879d5c43bb54054a0522b05213" +checksum = "2bdaa0fb40143b4b878256ac4e2b498885daafc269502504d91929eab4744bf4" dependencies = [ "cfg-if", "rustc-ap-arena", "rustc-ap-rustc_data_structures", + "rustc-ap-rustc_index", "rustc-ap-rustc_macros", "rustc-ap-serialize", "scoped-tls", @@ -3243,7 +3086,18 @@ dependencies = [ "crossbeam-deque 0.2.0", "either", - "rustc-rayon-core", + "rustc-rayon-core 0.2.0", +] + +[[package]] +name = "rustc-rayon" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f32767f90d938f1b7199a174ef249ae1924f6e5bbdb9d112fea141e016f25b3a" +dependencies = [ + "crossbeam-deque 0.7.1", + "either", + "rustc-rayon-core 0.3.0", ] [[package]] @@ -3259,10 +3113,17 @@ ] [[package]] -name = "rustc-serialize" -version = "0.3.24" +name = "rustc-rayon-core" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +checksum = "ea2427831f0053ea3ea73559c8eabd893133a51b251d142bacee53c62a288cb3" +dependencies = [ + "crossbeam-deque 0.7.1", + "crossbeam-queue", + "crossbeam-utils 0.6.5", + "lazy_static 1.3.0", + "num_cpus", +] [[package]] name = "rustc-std-workspace-alloc" @@ -3293,6 +3154,7 @@ "serde", "serde_json", "smallvec", + "syn 0.15.35", "url 2.1.0", "winapi 0.3.6", ] @@ -3317,19 +3179,6 @@ ] [[package]] -name = "rustc_ast_borrowck" -version = "0.0.0" -dependencies = [ - "graphviz", - "log", - "rustc", - "rustc_data_structures", - "rustc_errors", - "syntax", - "syntax_pos", -] - -[[package]] name = "rustc_codegen_llvm" version = "0.0.0" dependencies = [ @@ -3347,7 +3196,6 @@ "log", "memmap", "num_cpus", - "parking_lot 0.9.0", "rustc", "rustc_apfloat", "rustc_codegen_utils", @@ -3355,6 +3203,7 @@ "rustc_errors", "rustc_fs_util", "rustc_incremental", + "rustc_index", "rustc_target", "serialize", "syntax", @@ -3366,7 +3215,6 @@ name = "rustc_codegen_utils" version = "0.0.0" dependencies = [ - "flate2", "log", "punycode", "rustc", @@ -3392,8 +3240,9 @@ "log", "parking_lot 0.9.0", "rustc-hash", - "rustc-rayon", - "rustc-rayon-core", + "rustc-rayon 0.3.0", + "rustc-rayon-core 0.3.0", + "rustc_index", "serialize", "smallvec", "stable_deref_trait", @@ -3403,16 +3252,16 @@ name = "rustc_driver" version = "0.0.0" dependencies = [ - "env_logger", + "env_logger 0.7.1", "graphviz", "lazy_static 1.3.0", "log", "rustc", - "rustc_ast_borrowck", "rustc_codegen_utils", "rustc_data_structures", "rustc_errors", "rustc_interface", + "rustc_lint", "rustc_metadata", "rustc_mir", "rustc_plugin", @@ -3459,14 +3308,21 @@ ] [[package]] +name = "rustc_index" +version = "0.0.0" +dependencies = [ + "serialize", + "smallvec", +] + +[[package]] name = "rustc_interface" version = "0.0.0" dependencies = [ "log", "once_cell", "rustc", - "rustc-rayon", - "rustc_ast_borrowck", + "rustc-rayon 0.3.0", "rustc_codegen_ssa", "rustc_codegen_utils", "rustc_data_structures", @@ -3479,11 +3335,13 @@ "rustc_plugin_impl", "rustc_privacy", "rustc_resolve", + "rustc_target", "rustc_traits", "rustc_typeck", "serialize", "smallvec", "syntax", + "syntax_expand", "syntax_ext", "syntax_pos", "tempfile", @@ -3503,6 +3361,7 @@ "log", "rustc", "rustc_data_structures", + "rustc_index", "rustc_target", "syntax", "syntax_pos", @@ -3532,10 +3391,10 @@ version = "0.1.0" dependencies = [ "itertools 0.8.0", - "proc-macro2 0.4.30", - "quote 0.6.12", - "syn 0.15.35", - "synstructure", + "proc-macro2 1.0.3", + "quote 1.0.2", + "syn 1.0.5", + "synstructure 0.12.1", ] [[package]] @@ -3548,11 +3407,13 @@ "rustc", "rustc_data_structures", "rustc_errors", + "rustc_index", "rustc_target", "serialize", "smallvec", "stable_deref_trait", "syntax", + "syntax_expand", "syntax_pos", ] @@ -3561,7 +3422,6 @@ version = "0.0.0" dependencies = [ "arena", - "byteorder", "either", "graphviz", "log", @@ -3571,6 +3431,7 @@ "rustc_apfloat", "rustc_data_structures", "rustc_errors", + "rustc_index", "rustc_lexer", "rustc_target", "serialize", @@ -3598,6 +3459,8 @@ "rustc", "rustc_data_structures", "rustc_errors", + "rustc_index", + "rustc_target", "syntax", "syntax_pos", ] @@ -3614,9 +3477,9 @@ version = "0.0.0" dependencies = [ "rustc", - "rustc_errors", "rustc_metadata", "syntax", + "syntax_expand", "syntax_pos", ] @@ -3638,7 +3501,6 @@ dependencies = [ "arena", "bitflags", - "indexmap", "log", "rustc", "rustc_data_structures", @@ -3646,6 +3508,7 @@ "rustc_metadata", "smallvec", "syntax", + "syntax_expand", "syntax_pos", ] @@ -3660,7 +3523,6 @@ "rustc_codegen_utils", "rustc_data_structures", "rustc_target", - "rustc_typeck", "serde_json", "syntax", "syntax_pos", @@ -3673,6 +3535,7 @@ "bitflags", "log", "rustc_data_structures", + "rustc_index", "serialize", "syntax_pos", ] @@ -3691,9 +3554,7 @@ name = "rustc_traits" version = "0.0.0" dependencies = [ - "bitflags", "chalk-engine", - "graphviz", "log", "rustc", "rustc_data_structures", @@ -3723,6 +3584,7 @@ "rustc", "rustc_data_structures", "rustc_errors", + "rustc_index", "rustc_target", "smallvec", "syntax", @@ -3744,7 +3606,7 @@ dependencies = [ "minifier", "pulldown-cmark 0.5.3", - "rustc-rayon", + "rustc-rayon 0.3.0", "tempfile", ] @@ -3783,15 +3645,15 @@ [[package]] name = "rustfmt-nightly" -version = "1.4.8" +version = "1.4.9" dependencies = [ "annotate-snippets", "bytecount", - "cargo_metadata", + "cargo_metadata 0.8.0", "derive-new", "diff", "dirs", - "env_logger", + "env_logger 0.6.2", "failure", "getopts", "ignore", @@ -3806,7 +3668,7 @@ "rustfmt-config_proc_macro", "serde", "serde_json", - "structopt 0.3.1", + "structopt", "term 0.6.0", "toml", "unicode-segmentation", @@ -3864,27 +3726,6 @@ checksum = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" [[package]] -name = "security-framework" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2" -dependencies = [ - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56" -dependencies = [ - "core-foundation-sys", -] - -[[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3952,18 +3793,6 @@ ] [[package]] -name = "serde_urlencoded" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" -dependencies = [ - "dtoa", - "itoa", - "serde", - "url 1.7.2", -] - -[[package]] name = "serialize" version = "0.0.0" dependencies = [ @@ -4056,13 +3885,13 @@ dependencies = [ "alloc", "backtrace", - "cc", "cfg-if", "compiler_builtins", "core", "dlmalloc", "fortanix-sgx-abi", - "hashbrown", + "hashbrown 0.6.2", + "hermit-abi", "libc", "panic_abort", "panic_unwind", @@ -4077,15 +3906,6 @@ ] [[package]] -name = "string" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" -dependencies = [ - "bytes", -] - -[[package]] name = "string_cache" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4136,34 +3956,12 @@ [[package]] name = "structopt" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" -dependencies = [ - "clap", - "structopt-derive 0.2.18", -] - -[[package]] -name = "structopt" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ac9d6e93dd792b217bf89cda5c14566e3043960c6f9da890c2ba5d09d07804c" dependencies = [ "clap", - "structopt-derive 0.3.1", -] - -[[package]] -name = "structopt-derive" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" -dependencies = [ - "heck", - "proc-macro2 0.4.30", - "quote 0.6.12", - "syn 0.15.35", + "structopt-derive", ] [[package]] @@ -4232,6 +4030,18 @@ ] [[package]] +name = "synstructure" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" +dependencies = [ + "proc-macro2 1.0.3", + "quote 1.0.2", + "syn 1.0.5", + "unicode-xid 0.2.0", +] + +[[package]] name = "syntax" version = "0.0.0" dependencies = [ @@ -4240,8 +4050,8 @@ "log", "rustc_data_structures", "rustc_errors", + "rustc_index", "rustc_lexer", - "rustc_macros", "rustc_target", "scoped-tls", "serialize", @@ -4250,6 +4060,25 @@ ] [[package]] +name = "syntax_expand" +version = "0.0.0" +dependencies = [ + "bitflags", + "lazy_static 1.3.0", + "log", + "rustc_data_structures", + "rustc_errors", + "rustc_index", + "rustc_lexer", + "rustc_target", + "scoped-tls", + "serialize", + "smallvec", + "syntax", + "syntax_pos", +] + +[[package]] name = "syntax_ext" version = "0.0.0" dependencies = [ @@ -4257,10 +4086,10 @@ "log", "rustc_data_structures", "rustc_errors", - "rustc_lexer", "rustc_target", "smallvec", "syntax", + "syntax_expand", "syntax_pos", ] @@ -4271,6 +4100,7 @@ "arena", "cfg-if", "rustc_data_structures", + "rustc_index", "rustc_macros", "scoped-tls", "serialize", @@ -4278,12 +4108,6 @@ ] [[package]] -name = "take_mut" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" - -[[package]] name = "tar" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4330,16 +4154,6 @@ [[package]] name = "term" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" -dependencies = [ - "kernel32-sys", - "winapi 0.2.8", -] - -[[package]] -name = "term" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd90505d5006a4422d3520b30c781d480b3f36768c2fa2187c3e950bc110464" @@ -4395,17 +4209,6 @@ ] [[package]] -name = "tester" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e812cb26c597f86a49b26dbb58b878bd2a2b4b93fc069dc39499228fe556ff6" -dependencies = [ - "getopts", - "libc", - "term 0.4.6", -] - -[[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4469,17 +4272,6 @@ ] [[package]] -name = "tokio-buf" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" -dependencies = [ - "bytes", - "either", - "futures", -] - -[[package]] name = "tokio-codec" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4716,12 +4508,6 @@ ] [[package]] -name = "try-lock" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" - -[[package]] name = "typenum" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4741,15 +4527,6 @@ [[package]] name = "unicase" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicase" version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150" @@ -4868,15 +4645,6 @@ checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" [[package]] -name = "uuid" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" -dependencies = [ - "rand 0.6.1", -] - -[[package]] name = "vcpkg" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4926,17 +4694,6 @@ ] [[package]] -name = "want" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" -dependencies = [ - "futures", - "log", - "try-lock", -] - -[[package]] name = "wasi" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/README.md b/README.md index 96d7e93..c5468a2 100644 --- a/README.md +++ b/README.md
@@ -33,6 +33,7 @@ * `curl` * `git` * `ssl` which comes in `libssl-dev` or `openssl-devel` + * `pkg-config` if you are compiling on Linux and targeting Linux 2. Clone the [source] with `git`: @@ -243,19 +244,17 @@ To contribute to Rust, please see [CONTRIBUTING](CONTRIBUTING.md). -Rust has an [IRC] culture and most real-time collaboration happens in a -variety of channels on Mozilla's IRC network, irc.mozilla.org. The -most popular channel is [#rust], a venue for general discussion about -Rust. And a good place to ask for help would be [#rust-beginners]. +Most real-time collaboration happens in a variety of channels on the +[Rust Discord server][rust-discord], with channels dedicated for getting help, +community, documentation, and all major contribution areas in the Rust ecosystem. +A good place to ask for help would be the #help channel. The [rustc guide] might be a good place to start if you want to find out how various parts of the compiler work. Also, you may find the [rustdocs for the compiler itself][rustdocs] useful. -[IRC]: https://en.wikipedia.org/wiki/Internet_Relay_Chat -[#rust]: irc://irc.mozilla.org/rust -[#rust-beginners]: irc://irc.mozilla.org/rust-beginners +[rust-discord]: https://discord.gg/rust-lang [rustc guide]: https://rust-lang.github.io/rustc-guide/about-this-guide.html [rustdocs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/
diff --git a/config.toml.example b/config.toml.example index 848147c..e832570 100644 --- a/config.toml.example +++ b/config.toml.example
@@ -258,10 +258,9 @@ [rust] # Whether or not to optimize the compiler and standard library. -# -# Note: the slowness of the non optimized compiler compiling itself usually -# outweighs the time gains in not doing optimizations, therefore a -# full bootstrap takes much more time with `optimize` set to false. +# WARNING: Building with optimize = false is NOT SUPPORTED. Due to bootstrapping, +# building without optimizations takes much longer than optimizing. Further, some platforms +# fail to build without this optimization (c.f. #65352). #optimize = true # Indicates that the build should be configured for debugging Rust. A @@ -341,6 +340,9 @@ # nightly features #channel = "dev" +# The root location of the MUSL installation directory. +#musl-root = "..." + # By default the `rustc` executable is built with `-Wl,-rpath` flags on Unix # platforms to ensure that the compiler is usable by default from the build # directory (as it links to a number of dynamic libraries). This may not be @@ -374,9 +376,7 @@ # This is an array of the codegen backends that will be compiled for the rustc # that's being compiled. The default is to only build the LLVM codegen backend, -# but you can also optionally enable the "emscripten" backend for asm.js or -# make this an empty array (but that probably won't get too far in the -# bootstrap) +# and currently the only standard option supported is `"llvm"` #codegen-backends = ["llvm"] # This is the name of the directory in which codegen backends will get installed
diff --git a/git-commit-hash b/git-commit-hash index c702c5c..7047962 100644 --- a/git-commit-hash +++ b/git-commit-hash
@@ -1 +1 @@ -4560ea788cb760f0a34127156c78e2552949f734 \ No newline at end of file +73528e339aae0f17a15ffa49a8ac608f50c6cf14 \ No newline at end of file
diff --git a/src/README.md b/src/README.md index 32ca4a1..2f7cf90 100644 --- a/src/README.md +++ b/src/README.md
@@ -5,7 +5,4 @@ For more information on how various parts of the compiler work, see the [rustc guide]. -There is also useful content in this README: -https://github.com/rust-lang/rust/tree/master/src/librustc/infer/lexical_region_resolve. - [rustc guide]: https://rust-lang.github.io/rustc-guide/about-this-guide.html
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 3e877fc..c501378 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md
@@ -328,6 +328,8 @@ `Config` struct. * Adding a sanity check? Take a look at `bootstrap/sanity.rs`. -If you have any questions feel free to reach out on `#rust-infra` on IRC or ask on -internals.rust-lang.org. When you encounter bugs, please file issues on the -rust-lang/rust issue tracker. +If you have any questions feel free to reach out on `#infra` channel in the +[Rust Discord server][rust-discord] or ask on internals.rust-lang.org. When +you encounter bugs, please file issues on the rust-lang/rust issue tracker. + +[rust-discord]: https://discord.gg/rust-lang
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 65129ee..4caf36a 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py
@@ -734,10 +734,6 @@ if module.endswith("llvm-project"): if self.get_toml('llvm-config') and self.get_toml('lld') != 'true': continue - if module.endswith("llvm-emscripten"): - backends = self.get_toml('codegen-backends') - if backends is None or not 'emscripten' in backends: - continue check = self.check_submodule(module, slow_submodules) filtered_submodules.append((module, check)) submodules_names.append(module)
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 5d586f0..2edcef2 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs
@@ -443,6 +443,7 @@ dist::Rustc, dist::DebuggerScripts, dist::Std, + dist::RustcDev, dist::Analysis, dist::Src, dist::PlainSourceTarball, @@ -817,12 +818,22 @@ let mut rustflags = Rustflags::new(&target); if stage != 0 { + if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") { + cargo.args(s.split_whitespace()); + } rustflags.env("RUSTFLAGS_NOT_BOOTSTRAP"); } else { + if let Ok(s) = env::var("CARGOFLAGS_BOOTSTRAP") { + cargo.args(s.split_whitespace()); + } rustflags.env("RUSTFLAGS_BOOTSTRAP"); rustflags.arg("--cfg=bootstrap"); } + if let Ok(s) = env::var("CARGOFLAGS") { + cargo.args(s.split_whitespace()); + } + match mode { Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {}, Mode::Rustc | Mode::Codegen | Mode::ToolRustc => { @@ -875,7 +886,18 @@ // things still build right, please do! match mode { Mode::Std => metadata.push_str("std"), - _ => {}, + // When we're building rustc tools, they're built with a search path + // that contains things built during the rustc build. For example, + // bitflags is built during the rustc build, and is a dependency of + // rustdoc as well. We're building rustdoc in a different target + // directory, though, which means that Cargo will rebuild the + // dependency. When we go on to build rustdoc, we'll look for + // bitflags, and find two different copies: one built during the + // rustc step and one that we just built. This isn't always a + // problem, somehow -- not really clear why -- but we know that this + // fixes things. + Mode::ToolRustc => metadata.push_str("tool-rustc"), + _ => {} } cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata); @@ -970,6 +992,7 @@ Some("-Wl,-rpath,@loader_path/../lib") } else if !target.contains("windows") && !target.contains("wasm32") && + !target.contains("emscripten") && !target.contains("fuchsia") { Some("-Wl,-rpath,$ORIGIN/../lib") } else {
diff --git a/src/bootstrap/cache.rs b/src/bootstrap/cache.rs index 53071df..4310f2c 100644 --- a/src/bootstrap/cache.rs +++ b/src/bootstrap/cache.rs
@@ -161,7 +161,7 @@ } } -struct TyIntern<T: Hash + Clone + Eq> { +struct TyIntern<T: Clone + Eq> { items: Vec<T>, set: HashMap<T, Interned<T>>, }
diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index caa4843..ef1b6e2 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs
@@ -13,7 +13,7 @@ use crate::Build; // The version number -pub const CFG_RELEASE_NUM: &str = "1.39.0"; +pub const CFG_RELEASE_NUM: &str = "1.40.0"; pub struct GitInfo { inner: Option<Info>,
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index cadb9a7..df1c725 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs
@@ -55,6 +55,7 @@ cargo, args(builder.kind), &libstd_stamp(builder, compiler, target), + vec![], true); let libdir = builder.sysroot_libdir(compiler, target); @@ -103,6 +104,7 @@ cargo, args(builder.kind), &librustc_stamp(builder, compiler, target), + vec![], true); let libdir = builder.sysroot_libdir(compiler, target); @@ -155,6 +157,7 @@ cargo, args(builder.kind), &codegen_backend_stamp(builder, compiler, target, backend), + vec![], true); } } @@ -199,6 +202,7 @@ cargo, args(builder.kind), &rustdoc_stamp(builder, compiler, target), + vec![], true); let libdir = builder.sysroot_libdir(compiler, target);
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 6ea32ed..8e5fe25 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs
@@ -69,7 +69,7 @@ return; } - builder.ensure(StartupObjects { compiler, target }); + let mut target_deps = builder.ensure(StartupObjects { compiler, target }); let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); if compiler_to_use != compiler { @@ -91,7 +91,7 @@ return; } - copy_third_party_objects(builder, &compiler, target); + target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter()); let mut cargo = builder.cargo(compiler, Mode::Std, target, "build"); std_cargo(builder, &compiler, target, &mut cargo); @@ -102,6 +102,7 @@ cargo, vec![], &libstd_stamp(builder, compiler, target), + target_deps, false); builder.ensure(StdLink { @@ -113,9 +114,22 @@ } /// Copies third pary objects needed by various targets. -fn copy_third_party_objects(builder: &Builder<'_>, compiler: &Compiler, target: Interned<String>) { +fn copy_third_party_objects(builder: &Builder<'_>, compiler: &Compiler, target: Interned<String>) + -> Vec<PathBuf> +{ let libdir = builder.sysroot_libdir(*compiler, target); + let mut target_deps = vec![]; + + let mut copy_and_stamp = |sourcedir: &Path, name: &str| { + let target = libdir.join(name); + builder.copy( + &sourcedir.join(name), + &target, + ); + target_deps.push(target); + }; + // Copies the crt(1,i,n).o startup objects // // Since musl supports fully static linking, we can cross link for it even @@ -123,19 +137,13 @@ // files. As those shipped with glibc won't work, copy the ones provided by // musl so we have them on linux-gnu hosts. if target.contains("musl") { + let srcdir = builder.musl_root(target).unwrap().join("lib"); for &obj in &["crt1.o", "crti.o", "crtn.o"] { - builder.copy( - &builder.musl_root(target).unwrap().join("lib").join(obj), - &libdir.join(obj), - ); + copy_and_stamp(&srcdir, obj); } } else if target.ends_with("-wasi") { - for &obj in &["crt1.o"] { - builder.copy( - &builder.wasi_root(target).unwrap().join("lib/wasm32-wasi").join(obj), - &libdir.join(obj), - ); - } + let srcdir = builder.wasi_root(target).unwrap().join("lib/wasm32-wasi"); + copy_and_stamp(&srcdir, "crt1.o"); } // Copies libunwind.a compiled to be linked wit x86_64-fortanix-unknown-sgx. @@ -145,11 +153,11 @@ // which is provided by std for this target. if target == "x86_64-fortanix-unknown-sgx" { let src_path_env = "X86_FORTANIX_SGX_LIBS"; - let obj = "libunwind.a"; let src = env::var(src_path_env).expect(&format!("{} not found in env", src_path_env)); - let src = Path::new(&src).join(obj); - builder.copy(&src, &libdir.join(obj)); + copy_and_stamp(Path::new(&src), "libunwind.a"); } + + target_deps } /// Configure cargo to compile the standard library, adding appropriate env vars @@ -210,7 +218,6 @@ // config.toml equivalent) is used let llvm_config = builder.ensure(native::Llvm { target: builder.config.build, - emscripten: false, }); cargo.env("LLVM_CONFIG", llvm_config); cargo.env("RUSTC_BUILD_SANITIZERS", "1"); @@ -307,7 +314,7 @@ } impl Step for StartupObjects { - type Output = (); + type Output = Vec<PathBuf>; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/rtstartup") @@ -326,13 +333,15 @@ /// They don't require any library support as they're just plain old object /// files, so we just use the nightly snapshot compiler to always build them (as /// no other compilers are guaranteed to be available). - fn run(self, builder: &Builder<'_>) { + fn run(self, builder: &Builder<'_>) -> Vec<PathBuf> { let for_compiler = self.compiler; let target = self.target; if !target.contains("windows-gnu") { - return + return vec![] } + let mut target_deps = vec![]; + let src_dir = &builder.src.join("src/rtstartup"); let dst_dir = &builder.native_dir(target).join("rtstartup"); let sysroot_dir = &builder.sysroot_libdir(for_compiler, target); @@ -351,7 +360,9 @@ .arg(src_file)); } - builder.copy(dst_file, &sysroot_dir.join(file.to_string() + ".o")); + let target = sysroot_dir.join(file.to_string() + ".o"); + builder.copy(dst_file, &target); + target_deps.push(target); } for obj in ["crt2.o", "dllcrt2.o"].iter() { @@ -359,8 +370,12 @@ builder.cc(target), target, obj); - builder.copy(&src, &sysroot_dir.join(obj)); + let target = sysroot_dir.join(obj); + builder.copy(&src, &target); + target_deps.push(target); } + + target_deps } } @@ -438,6 +453,7 @@ cargo, vec![], &librustc_stamp(builder, compiler, target), + vec![], false); builder.ensure(RustcLink { @@ -586,7 +602,7 @@ let tmp_stamp = out_dir.join(".tmp.stamp"); - let files = run_cargo(builder, cargo, vec![], &tmp_stamp, false); + let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false); if builder.config.dry_run { return; } @@ -615,46 +631,37 @@ compiler: &Compiler, target: Interned<String>, backend: Interned<String>) -> String { - let mut features = String::new(); - match &*backend { - "llvm" | "emscripten" => { + "llvm" => { // Build LLVM for our target. This will implicitly build the // host LLVM if necessary. let llvm_config = builder.ensure(native::Llvm { target, - emscripten: backend == "emscripten", }); - if backend == "emscripten" { - features.push_str(" emscripten"); - } - builder.info(&format!("Building stage{} codegen artifacts ({} -> {}, {})", compiler.stage, &compiler.host, target, backend)); // Pass down configuration from the LLVM build into the build of // librustc_llvm and librustc_codegen_llvm. - if builder.is_rust_llvm(target) && backend != "emscripten" { + if builder.is_rust_llvm(target) { cargo.env("LLVM_RUSTLLVM", "1"); } cargo.env("LLVM_CONFIG", &llvm_config); - if backend != "emscripten" { - let target_config = builder.config.target_config.get(&target); - if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { - cargo.env("CFG_LLVM_ROOT", s); - } + let target_config = builder.config.target_config.get(&target); + if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { + cargo.env("CFG_LLVM_ROOT", s); } // Some LLVM linker flags (-L and -l) may be needed to link librustc_llvm. if let Some(ref s) = builder.config.llvm_ldflags { cargo.env("LLVM_LINKER_FLAGS", s); } - // Building with a static libstdc++ is only supported on linux right now, + // Building with a static libstdc++ is only supported on linux and mingw right now, // not for MSVC or macOS if builder.config.llvm_static_stdcpp && !target.contains("freebsd") && - !target.contains("windows") && + !target.contains("msvc") && !target.contains("apple") { let file = compiler_file(builder, builder.cxx(target).unwrap(), @@ -662,9 +669,7 @@ "libstdc++.a"); cargo.env("LLVM_STATIC_STDCPP", file); } - if builder.config.llvm_link_shared || - (builder.config.llvm_thin_lto && backend != "emscripten") - { + if builder.config.llvm_link_shared || builder.config.llvm_thin_lto { cargo.env("LLVM_LINK_SHARED", "1"); } if builder.config.llvm_use_libcxx { @@ -676,8 +681,7 @@ } _ => panic!("unknown backend: {}", backend), } - - features + String::new() } /// Creates the `codegen-backends` folder for a compiler that's about to be @@ -954,6 +958,7 @@ cargo: Cargo, tail_args: Vec<String>, stamp: &Path, + additional_target_deps: Vec<PathBuf>, is_check: bool) -> Vec<PathBuf> { @@ -1070,6 +1075,7 @@ deps.push((path_to_add.into(), false)); } + deps.extend(additional_target_deps.into_iter().map(|d| (d, false))); deps.sort(); let mut new_contents = Vec::new(); for (dep, proc_macro) in deps.iter() {
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 52b5cd8..d1bdfa0 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs
@@ -200,16 +200,15 @@ target: Vec<String>, cargo: Option<String>, rustc: Option<String>, - low_priority: Option<bool>, - compiler_docs: Option<bool>, docs: Option<bool>, + compiler_docs: Option<bool>, submodules: Option<bool>, fast_submodules: Option<bool>, gdb: Option<String>, - locked_deps: Option<bool>, - vendor: Option<bool>, nodejs: Option<String>, python: Option<String>, + locked_deps: Option<bool>, + vendor: Option<bool>, full_bootstrap: Option<bool>, extended: Option<bool>, tools: Option<HashSet<String>>, @@ -217,6 +216,7 @@ sanitizers: Option<bool>, profiler: Option<bool>, cargo_native_static: Option<bool>, + low_priority: Option<bool>, configure_args: Option<Vec<String>>, local_rebuild: Option<bool>, print_step_timings: Option<bool>, @@ -228,11 +228,11 @@ struct Install { prefix: Option<String>, sysconfdir: Option<String>, - datadir: Option<String>, docdir: Option<String>, bindir: Option<String>, libdir: Option<String>, mandir: Option<String>, + datadir: Option<String>, // standard paths, currently unused infodir: Option<String>, @@ -243,14 +243,14 @@ #[derive(Deserialize, Default)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct Llvm { - ccache: Option<StringOrBool>, - ninja: Option<bool>, - assertions: Option<bool>, optimize: Option<bool>, thin_lto: Option<bool>, release_debuginfo: Option<bool>, + assertions: Option<bool>, + ccache: Option<StringOrBool>, version_check: Option<bool>, static_libstdcpp: Option<bool>, + ninja: Option<bool>, targets: Option<String>, experimental_targets: Option<String>, link_jobs: Option<u32>, @@ -293,6 +293,7 @@ #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct Rust { optimize: Option<bool>, + debug: Option<bool>, codegen_units: Option<u32>, codegen_units_std: Option<u32>, debug_assertions: Option<bool>, @@ -301,25 +302,24 @@ debuginfo_level_std: Option<u32>, debuginfo_level_tools: Option<u32>, debuginfo_level_tests: Option<u32>, - parallel_compiler: Option<bool>, backtrace: Option<bool>, + incremental: Option<bool>, + parallel_compiler: Option<bool>, default_linker: Option<String>, channel: Option<String>, musl_root: Option<String>, rpath: Option<bool>, + verbose_tests: Option<bool>, optimize_tests: Option<bool>, codegen_tests: Option<bool>, ignore_git: Option<bool>, - debug: Option<bool>, dist_src: Option<bool>, - verbose_tests: Option<bool>, - incremental: Option<bool>, save_toolstates: Option<String>, codegen_backends: Option<Vec<String>>, codegen_backends_dir: Option<String>, lld: Option<bool>, - lldb: Option<bool>, llvm_tools: Option<bool>, + lldb: Option<bool>, deny_warnings: Option<bool>, backtrace_on_ice: Option<bool>, verify_llvm_ir: Option<bool>, @@ -333,13 +333,13 @@ #[derive(Deserialize, Default)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct TomlTarget { - llvm_config: Option<String>, - llvm_filecheck: Option<String>, cc: Option<String>, cxx: Option<String>, ar: Option<String>, ranlib: Option<String>, linker: Option<String>, + llvm_config: Option<String>, + llvm_filecheck: Option<String>, android_ndk: Option<String>, crt_static: Option<bool>, musl_root: Option<String>, @@ -668,7 +668,6 @@ pub fn llvm_enabled(&self) -> bool { self.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) - || self.rust_codegen_backends.contains(&INTERNER.intern_str("emscripten")) } }
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 346f0cb..bb6041d 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py
@@ -55,7 +55,6 @@ o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball") o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo") o("profiler", "build.profiler", "build the profiler runtime") -o("emscripten", None, "compile the emscripten backend as well as LLVM") o("full-tools", None, "enable all tools") o("lld", "rust.lld", "build lld") o("lldb", "rust.lldb", "build lldb") @@ -134,6 +133,10 @@ "mips-unknown-linux-musl install directory") v("musl-root-mipsel", "target.mipsel-unknown-linux-musl.musl-root", "mipsel-unknown-linux-musl install directory") +v("musl-root-mips64", "target.mips64-unknown-linux-muslabi64.musl-root", + "mips64-unknown-linux-muslabi64 install directory") +v("musl-root-mips64el", "target.mips64el-unknown-linux-muslabi64.musl-root", + "mips64el-unknown-linux-muslabi64 install directory") v("qemu-armhf-rootfs", "target.arm-unknown-linux-gnueabihf.qemu-rootfs", "rootfs in qemu testing, you probably don't want to use this") v("qemu-aarch64-rootfs", "target.aarch64-unknown-linux-gnu.qemu-rootfs", @@ -335,10 +338,8 @@ set('build.host', value.split(',')) elif option.name == 'target': set('build.target', value.split(',')) - elif option.name == 'emscripten': - set('rust.codegen-backends', ['llvm', 'emscripten']) elif option.name == 'full-tools': - set('rust.codegen-backends', ['llvm', 'emscripten']) + set('rust.codegen-backends', ['llvm']) set('rust.lld', True) set('rust.llvm-tools', True) set('build.extended', True)
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index d9dff77..67907bc 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs
@@ -236,7 +236,7 @@ } let target_tools = ["gcc.exe", "ld.exe", "dlltool.exe", "libwinpthread-1.dll"]; - let mut rustc_dlls = vec!["libstdc++-6.dll", "libwinpthread-1.dll"]; + let mut rustc_dlls = vec!["libwinpthread-1.dll"]; if target_triple.starts_with("i686-") { rustc_dlls.push("libgcc_s_dw2-1.dll"); } else { @@ -637,6 +637,28 @@ } } +fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool { + // The only true set of target libraries came from the build triple, so + // let's reduce redundant work by only producing archives from that host. + if compiler.host != builder.config.build { + builder.info("\tskipping, not a build host"); + true + } else { + false + } +} + +/// Copy stamped files into an image's `target/lib` directory. +fn copy_target_libs(builder: &Builder<'_>, target: &str, image: &Path, stamp: &Path) { + let dst = image.join("lib/rustlib").join(target).join("lib"); + t!(fs::create_dir_all(&dst)); + for (path, host) in builder.read_stamp_file(stamp) { + if !host || builder.config.build == target { + builder.copy(&path, &dst.join(path.file_name().unwrap())); + } + } +} + #[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Std { pub compiler: Compiler, @@ -667,44 +689,19 @@ let target = self.target; let name = pkgname(builder, "rust-std"); - - // The only true set of target libraries came from the build triple, so - // let's reduce redundant work by only producing archives from that host. - if compiler.host != builder.config.build { - builder.info("\tskipping, not a build host"); - return distdir(builder).join(format!("{}-{}.tar.gz", name, target)); + let archive = distdir(builder).join(format!("{}-{}.tar.gz", name, target)); + if skip_host_target_lib(builder, compiler) { + return archive; } - // We want to package up as many target libraries as possible - // for the `rust-std` package, so if this is a host target we - // depend on librustc and otherwise we just depend on libtest. - if builder.hosts.iter().any(|t| t == target) { - builder.ensure(compile::Rustc { compiler, target }); - } else { - builder.ensure(compile::Std { compiler, target }); - } + builder.ensure(compile::Std { compiler, target }); let image = tmpdir(builder).join(format!("{}-{}-image", name, target)); let _ = fs::remove_dir_all(&image); - let dst = image.join("lib/rustlib").join(target); - t!(fs::create_dir_all(&dst)); - let mut src = builder.sysroot_libdir(compiler, target).to_path_buf(); - src.pop(); // Remove the trailing /lib folder from the sysroot_libdir - builder.cp_filtered(&src, &dst, &|path| { - if let Some(name) = path.file_name().and_then(|s| s.to_str()) { - if name == builder.config.rust_codegen_backends_dir.as_str() { - return false - } - if name == "bin" { - return false - } - if name.contains("LLVM") { - return false - } - } - true - }); + let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); + let stamp = compile::libstd_stamp(builder, compiler_to_use, target); + copy_target_libs(builder, &target, &image, &stamp); let mut cmd = rust_installer(builder); cmd.arg("generate") @@ -723,7 +720,73 @@ let _time = timeit(builder); builder.run(&mut cmd); builder.remove_dir(&image); - distdir(builder).join(format!("{}-{}.tar.gz", name, target)) + archive + } +} + +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] +pub struct RustcDev { + pub compiler: Compiler, + pub target: Interned<String>, +} + +impl Step for RustcDev { + type Output = PathBuf; + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("rustc-dev") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(RustcDev { + compiler: run.builder.compiler_for( + run.builder.top_stage, + run.builder.config.build, + run.target, + ), + target: run.target, + }); + } + + fn run(self, builder: &Builder<'_>) -> PathBuf { + let compiler = self.compiler; + let target = self.target; + + let name = pkgname(builder, "rustc-dev"); + let archive = distdir(builder).join(format!("{}-{}.tar.gz", name, target)); + if skip_host_target_lib(builder, compiler) { + return archive; + } + + builder.ensure(compile::Rustc { compiler, target }); + + let image = tmpdir(builder).join(format!("{}-{}-image", name, target)); + let _ = fs::remove_dir_all(&image); + + let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); + let stamp = compile::librustc_stamp(builder, compiler_to_use, target); + copy_target_libs(builder, &target, &image, &stamp); + + let mut cmd = rust_installer(builder); + cmd.arg("generate") + .arg("--product-name=Rust") + .arg("--rel-manifest-dir=rustlib") + .arg("--success-message=Rust-is-ready-to-develop.") + .arg("--image-dir").arg(&image) + .arg("--work-dir").arg(&tmpdir(builder)) + .arg("--output-dir").arg(&distdir(builder)) + .arg(format!("--package-name={}-{}", name, target)) + .arg(format!("--component-name=rustc-dev-{}", target)) + .arg("--legacy-manifest-dirs=rustlib,cargo"); + + builder.info(&format!("Dist rustc-dev stage{} ({} -> {})", + compiler.stage, &compiler.host, target)); + let _time = timeit(builder); + builder.run(&mut cmd); + builder.remove_dir(&image); + archive } } @@ -826,7 +889,6 @@ const LLVM_TEST: &[&str] = &[ "llvm-project/llvm/test", "llvm-project\\llvm\\test", - "llvm-emscripten/test", "llvm-emscripten\\test", ]; if LLVM_TEST.iter().any(|path| spath.contains(path)) && (spath.ends_with(".ll") || @@ -834,9 +896,6 @@ spath.ends_with(".s")) { return false } - if spath.contains("test/emscripten") || spath.contains("test\\emscripten") { - return false - } let full_path = Path::new(dir).join(path); if exclude_dirs.iter().any(|excl| full_path == Path::new(excl)) {
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 9203a55..39d7ea9 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs
@@ -160,7 +160,7 @@ } } -#[cfg(any(target_os = "haiku", not(any(unix, windows))))] +#[cfg(any(target_os = "haiku", target_os = "hermit", not(any(unix, windows))))] mod job { pub unsafe fn setup(_build: &mut crate::Build) { } @@ -232,7 +232,6 @@ miri_info: channel::GitInfo, rustfmt_info: channel::GitInfo, in_tree_llvm_info: channel::GitInfo, - emscripten_llvm_info: channel::GitInfo, local_rebuild: bool, fail_fast: bool, doc_tests: DocTests, @@ -351,7 +350,6 @@ // we always try to use git for LLVM builds let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project")); - let emscripten_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-emscripten")); let mut build = Build { initial_rustc: config.initial_rustc.clone(), @@ -376,7 +374,6 @@ miri_info, rustfmt_info, in_tree_llvm_info, - emscripten_llvm_info, cc: HashMap::new(), cxx: HashMap::new(), ar: HashMap::new(), @@ -553,10 +550,6 @@ self.out.join(&*target).join("llvm") } - fn emscripten_llvm_out(&self, target: Interned<String>) -> PathBuf { - self.out.join(&*target).join("llvm-emscripten") - } - fn lld_out(&self, target: Interned<String>) -> PathBuf { self.out.join(&*target).join("lld") } @@ -1087,6 +1080,10 @@ /// done. The file is updated immediately after this function completes. pub fn save_toolstate(&self, tool: &str, state: ToolState) { if let Some(ref path) = self.config.save_toolstates { + if let Some(parent) = path.parent() { + // Ensure the parent directory always exists + t!(std::fs::create_dir_all(parent)); + } let mut file = t!(fs::OpenOptions::new() .create(true) .read(true) @@ -1126,7 +1123,7 @@ } let mut paths = Vec::new(); - let contents = t!(fs::read(stamp)); + let contents = t!(fs::read(stamp), &stamp); // This is the method we use for extracting paths from the stamp file passed to us. See // run_cargo for more information (in compile.rs). for part in contents.split(|b| *b == 0) { @@ -1144,6 +1141,7 @@ pub fn copy(&self, src: &Path, dst: &Path) { if self.config.dry_run { return; } self.verbose_than(1, &format!("Copy {:?} to {:?}", src, dst)); + if src == dst { return; } let _ = fs::remove_file(&dst); let metadata = t!(src.symlink_metadata()); if metadata.file_type().is_symlink() {
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 7bf9ea2..2e89fd5 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs
@@ -28,7 +28,6 @@ #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Llvm { pub target: Interned<String>, - pub emscripten: bool, } impl Step for Llvm { @@ -40,46 +39,35 @@ run.path("src/llvm-project") .path("src/llvm-project/llvm") .path("src/llvm") - .path("src/llvm-emscripten") } fn make_run(run: RunConfig<'_>) { - let emscripten = run.path.ends_with("llvm-emscripten"); run.builder.ensure(Llvm { target: run.target, - emscripten, }); } /// Compile LLVM for `target`. fn run(self, builder: &Builder<'_>) -> PathBuf { let target = self.target; - let emscripten = self.emscripten; // If we're using a custom LLVM bail out here, but we can only use a // custom LLVM for the build triple. - if !self.emscripten { - if let Some(config) = builder.config.target_config.get(&target) { - if let Some(ref s) = config.llvm_config { - check_llvm_version(builder, s); - return s.to_path_buf() - } + if let Some(config) = builder.config.target_config.get(&target) { + if let Some(ref s) = config.llvm_config { + check_llvm_version(builder, s); + return s.to_path_buf() } } - let (llvm_info, root, out_dir, llvm_config_ret_dir) = if emscripten { - let info = &builder.emscripten_llvm_info; - let dir = builder.emscripten_llvm_out(target); - let config_dir = dir.join("bin"); - (info, "src/llvm-emscripten", dir, config_dir) - } else { - let info = &builder.in_tree_llvm_info; - let mut dir = builder.llvm_out(builder.config.build); - if !builder.config.build.contains("msvc") || builder.config.ninja { - dir.push("build"); - } - (info, "src/llvm-project/llvm", builder.llvm_out(target), dir.join("bin")) - }; + let llvm_info = &builder.in_tree_llvm_info; + let root = "src/llvm-project/llvm"; + let out_dir = builder.llvm_out(target); + let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build); + if !builder.config.build.contains("msvc") || builder.config.ninja { + llvm_config_ret_dir.push("build"); + } + llvm_config_ret_dir.push("bin"); let build_llvm_config = llvm_config_ret_dir .join(exe("llvm-config", &*builder.config.build)); @@ -107,8 +95,7 @@ } } - let descriptor = if emscripten { "Emscripten " } else { "" }; - builder.info(&format!("Building {}LLVM for {}", descriptor, target)); + builder.info(&format!("Building LLVM for {}", target)); let _time = util::timeit(&builder); t!(fs::create_dir_all(&out_dir)); @@ -123,23 +110,15 @@ // NOTE: remember to also update `config.toml.example` when changing the // defaults! - let llvm_targets = if self.emscripten { - "JSBackend" - } else { - match builder.config.llvm_targets { - Some(ref s) => s, - None => "AArch64;ARM;Hexagon;MSP430;Mips;NVPTX;PowerPC;RISCV;\ - Sparc;SystemZ;WebAssembly;X86", - } + let llvm_targets = match &builder.config.llvm_targets { + Some(s) => s, + None => "AArch64;ARM;Hexagon;MSP430;Mips;NVPTX;PowerPC;RISCV;\ + Sparc;SystemZ;WebAssembly;X86", }; - let llvm_exp_targets = if self.emscripten { - "" - } else { - match builder.config.llvm_experimental_targets { - Some(ref s) => s, - None => "", - } + let llvm_exp_targets = match builder.config.llvm_experimental_targets { + Some(ref s) => s, + None => "", }; let assertions = if builder.config.llvm_assertions {"ON"} else {"OFF"}; @@ -157,40 +136,30 @@ .define("WITH_POLLY", "OFF") .define("LLVM_ENABLE_TERMINFO", "OFF") .define("LLVM_ENABLE_LIBEDIT", "OFF") + .define("LLVM_ENABLE_BINDINGS", "OFF") .define("LLVM_ENABLE_Z3_SOLVER", "OFF") .define("LLVM_PARALLEL_COMPILE_JOBS", builder.jobs().to_string()) .define("LLVM_TARGET_ARCH", target.split('-').next().unwrap()) .define("LLVM_DEFAULT_TARGET_TRIPLE", target); - if builder.config.llvm_thin_lto && !emscripten { + if builder.config.llvm_thin_lto { cfg.define("LLVM_ENABLE_LTO", "Thin"); if !target.contains("apple") { cfg.define("LLVM_ENABLE_LLD", "ON"); } } - // By default, LLVM will automatically find OCaml and, if it finds it, - // install the LLVM bindings in LLVM_OCAML_INSTALL_PATH, which defaults - // to /usr/bin/ocaml. - // This causes problem for non-root builds of Rust. Side-step the issue - // by setting LLVM_OCAML_INSTALL_PATH to a relative path, so it installs - // in the prefix. - cfg.define("LLVM_OCAML_INSTALL_PATH", - env::var_os("LLVM_OCAML_INSTALL_PATH").unwrap_or_else(|| "usr/lib/ocaml".into())); - - let want_lldb = builder.config.lldb_enabled && !self.emscripten; - // This setting makes the LLVM tools link to the dynamic LLVM library, // which saves both memory during parallel links and overall disk space // for the tools. We don't do this on every platform as it doesn't work // equally well everywhere. - if builder.llvm_link_tools_dynamically(target) && !emscripten { + if builder.llvm_link_tools_dynamically(target) { cfg.define("LLVM_LINK_LLVM_DYLIB", "ON"); } // For distribution we want the LLVM tools to be *statically* linked to libstdc++ - if builder.config.llvm_tools_enabled || want_lldb { - if !target.contains("windows") { + if builder.config.llvm_tools_enabled || builder.config.lldb_enabled { + if !target.contains("msvc") { if target.contains("apple") { cfg.define("CMAKE_EXE_LINKER_FLAGS", "-static-libstdc++"); } else { @@ -217,7 +186,7 @@ enabled_llvm_projects.push("compiler-rt"); } - if want_lldb { + if builder.config.lldb_enabled { enabled_llvm_projects.push("clang"); enabled_llvm_projects.push("lldb"); // For the time being, disable code signing. @@ -242,10 +211,9 @@ } // http://llvm.org/docs/HowToCrossCompileLLVM.html - if target != builder.config.build && !emscripten { + if target != builder.config.build { builder.ensure(Llvm { target: builder.config.build, - emscripten: false, }); // FIXME: if the llvm root for the build triple is overridden then we // should use llvm-tblgen from there, also should verify that it @@ -427,7 +395,7 @@ cfg.define("CMAKE_C_FLAGS", cflags); let mut cxxflags = builder.cflags(target, GitRepo::Llvm).join(" "); if builder.config.llvm_static_stdcpp && - !target.contains("windows") && + !target.contains("msvc") && !target.contains("netbsd") { cxxflags.push_str(" -static-libstdc++"); @@ -489,7 +457,6 @@ let llvm_config = builder.ensure(Llvm { target: self.target, - emscripten: false, }); let out_dir = builder.lld_out(target); @@ -567,6 +534,10 @@ builder.info("Building test helpers"); t!(fs::create_dir_all(&dst)); let mut cfg = cc::Build::new(); + // FIXME: Workaround for https://github.com/emscripten-core/emscripten/issues/9013 + if target.contains("emscripten") { + cfg.pic(false); + } // We may have found various cross-compilers a little differently due to our // extra configuration, so inform gcc of these compilers. Note, though, that
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index b7ce9c7..60f0dcc 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs
@@ -386,8 +386,17 @@ extra_features: Vec::new(), }); if let Some(miri) = miri { + let mut cargo = builder.cargo(compiler, Mode::ToolRustc, host, "install"); + cargo.arg("xargo"); + // Configure `cargo install` path. cargo adds a `bin/`. + cargo.env("CARGO_INSTALL_ROOT", &builder.out); + + let mut cargo = Command::from(cargo); + if !try_run(builder, &mut cargo) { + return; + } + // # Run `cargo miri setup`. - // As a side-effect, this will install xargo. let mut cargo = tool::prepare_tool_cargo( builder, compiler, @@ -412,9 +421,7 @@ cargo.env("XARGO_RUST_SRC", builder.src.join("src")); // Debug things. cargo.env("RUST_BACKTRACE", "1"); - // Configure `cargo install` path, and let cargo-miri know that that's where - // xargo ends up. - cargo.env("CARGO_INSTALL_ROOT", &builder.out); // cargo adds a `bin/` + // Let cargo-miri know where xargo ended up. cargo.env("XARGO", builder.out.join("bin").join("xargo")); let mut cargo = Command::from(cargo); @@ -427,7 +434,7 @@ // (We do this separately from the above so that when the setup actually // happens we get some output.) // We re-use the `cargo` from above. - cargo.arg("--env"); + cargo.arg("--print-sysroot"); // FIXME: Is there a way in which we can re-use the usual `run` helpers? let miri_sysroot = if builder.config.dry_run { @@ -437,13 +444,11 @@ let out = cargo.output() .expect("We already ran `cargo miri setup` before and that worked"); assert!(out.status.success(), "`cargo miri setup` returned with non-0 exit code"); - // Output is "MIRI_SYSROOT=<str>\n". + // Output is "<sysroot>\n". let stdout = String::from_utf8(out.stdout) .expect("`cargo miri setup` stdout is not valid UTF-8"); - let stdout = stdout.trim(); - builder.verbose(&format!("`cargo miri setup --env` returned: {:?}", stdout)); - let sysroot = stdout.splitn(2, '=') - .nth(1).expect("`cargo miri setup` stdout did not contain '='"); + let sysroot = stdout.trim_end(); + builder.verbose(&format!("`cargo miri setup --print-sysroot` said: {:?}", sysroot)); sysroot.to_owned() }; @@ -1047,10 +1052,11 @@ // Also provide `rust_test_helpers` for the host. builder.ensure(native::TestHelpers { target: compiler.host }); - // wasm32 can't build the test helpers - if !target.contains("wasm32") { + // As well as the target, except for plain wasm32, which can't build it + if !target.contains("wasm32") || target.contains("emscripten") { builder.ensure(native::TestHelpers { target }); } + builder.ensure(RemoteCopyLibs { compiler, target }); let mut cmd = builder.tool_cmd(Tool::Compiletest); @@ -1164,7 +1170,7 @@ }).to_string() }) }; - let lldb_exe = if builder.config.lldb_enabled && !target.contains("emscripten") { + let lldb_exe = if builder.config.lldb_enabled { // Test against the lldb that was just built. builder.llvm_out(target).join("bin").join("lldb") } else { @@ -1233,7 +1239,6 @@ if builder.config.llvm_enabled() { let llvm_config = builder.ensure(native::Llvm { target: builder.config.build, - emscripten: false, }); if !builder.config.dry_run { let llvm_version = output(Command::new(&llvm_config).arg("--version"));
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index f1baeaf..8154980 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs
@@ -244,6 +244,7 @@ path.ends_with("rls") || path.ends_with("clippy") || path.ends_with("miri") || + path.ends_with("rustbook") || path.ends_with("rustfmt") { cargo.env("LIBZ_SYS_STATIC", "1");
diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs index f035a71..bb94fb2 100644 --- a/src/build_helper/lib.rs +++ b/src/build_helper/lib.rs
@@ -21,6 +21,13 @@ Err(e) => panic!("{} failed with {}", stringify!($e), e), } }; + // it can show extra info in the second parameter + ($e:expr, $extra:expr) => { + match $e { + Ok(e) => e, + Err(e) => panic!("{} failed with {} ({:?})", stringify!($e), e, $extra), + } + }; } // Because Cargo adds the compiler's dylib path to our library search path, llvm-config may
diff --git a/src/ci/azure-pipelines/auto.yml b/src/ci/azure-pipelines/auto.yml index e81b7ef..271c325 100644 --- a/src/ci/azure-pipelines/auto.yml +++ b/src/ci/azure-pipelines/auto.yml
@@ -130,6 +130,8 @@ IMAGE: i686-gnu-nopt test-various: IMAGE: test-various + wasm32: + IMAGE: wasm32 x86_64-gnu: IMAGE: x86_64-gnu x86_64-gnu-full-bootstrap: @@ -138,6 +140,7 @@ IMAGE: x86_64-gnu-aux x86_64-gnu-tools: IMAGE: x86_64-gnu-tools + DEPLOY_TOOLSTATES_JSON: toolstates-linux.json x86_64-gnu-debug: IMAGE: x86_64-gnu-debug x86_64-gnu-nopt: @@ -260,8 +263,9 @@ # MSVC tools tests x86_64-msvc-tools: MSYS_BITS: 64 - SCRIPT: src/ci/docker/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstates.json windows - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --save-toolstates=/tmp/toolstates.json + SCRIPT: src/ci/docker/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstate/toolstates.json windows + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --save-toolstates=/tmp/toolstate/toolstates.json + DEPLOY_TOOLSTATES_JSON: toolstates-windows.json # 32/64-bit MinGW builds. # @@ -313,6 +317,7 @@ # 32/64 bit MSVC and GNU deployment dist-x86_64-msvc: + MSYS_BITS: 64 RUST_CONFIGURE_ARGS: > --build=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc,aarch64-pc-windows-msvc @@ -322,6 +327,7 @@ DIST_REQUIRE_ALL_TOOLS: 1 DEPLOY: 1 dist-i686-msvc: + MSYS_BITS: 32 RUST_CONFIGURE_ARGS: > --build=i686-pc-windows-msvc --target=i586-pc-windows-msvc
diff --git a/src/ci/azure-pipelines/pr.yml b/src/ci/azure-pipelines/pr.yml index 62e23ef..566e654 100644 --- a/src/ci/azure-pipelines/pr.yml +++ b/src/ci/azure-pipelines/pr.yml
@@ -22,14 +22,6 @@ IMAGE: x86_64-gnu-llvm-6.0 mingw-check: IMAGE: mingw-check - -- job: LinuxTools - timeoutInMinutes: 600 - pool: - vmImage: ubuntu-16.04 - steps: - - template: steps/run.yml - parameters: - only_on_updated_submodules: 'yes' - variables: - IMAGE: x86_64-gnu-tools + x86_64-gnu-tools: + IMAGE: x86_64-gnu-tools + CI_ONLY_WHEN_SUBMODULES_CHANGED: 1
diff --git a/src/ci/azure-pipelines/steps/install-clang.yml b/src/ci/azure-pipelines/steps/install-clang.yml deleted file mode 100644 index 14daf81..0000000 --- a/src/ci/azure-pipelines/steps/install-clang.yml +++ /dev/null
@@ -1,46 +0,0 @@ -steps: - -- bash: | - set -e - curl -f http://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz | tar xJf - - - export CC=`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/bin/clang - echo "##vso[task.setvariable variable=CC]$CC" - - export CXX=`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/bin/clang++ - echo "##vso[task.setvariable variable=CXX]$CXX" - - # Configure `AR` specifically so rustbuild doesn't try to infer it as - # `clang-ar` by accident. - echo "##vso[task.setvariable variable=AR]ar" - displayName: Install clang (OSX) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin')) - -# If we're compiling for MSVC then we, like most other distribution builders, -# switch to clang as the compiler. This'll allow us eventually to enable LTO -# amongst LLVM and rustc. Note that we only do this on MSVC as I don't think -# clang has an output mode compatible with MinGW that we need. If it does we -# should switch to clang for MinGW as well! -# -# Note that the LLVM installer is an NSIS installer -# -# Original downloaded here came from -# http://releases.llvm.org/7.0.0/LLVM-7.0.0-win64.exe -# That installer was run through `wine` on Linux and then the resulting -# installation directory (found in `$HOME/.wine/drive_c/Program Files/LLVM`) was -# packaged up into a tarball. We've had issues otherwise that the installer will -# randomly hang, provide not a lot of useful information, pollute global state, -# etc. In general the tarball is just more confined and easier to deal with when -# working with various CI environments. -- bash: | - set -e - mkdir -p citools - cd citools - curl -f https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc/LLVM-7.0.0-win64.tar.gz | tar xzf - - echo "##vso[task.setvariable variable=RUST_CONFIGURE_ARGS]$RUST_CONFIGURE_ARGS --set llvm.clang-cl=`pwd`/clang-rust/bin/clang-cl.exe" - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['MINGW_URL'],'')) - displayName: Install clang (Windows) - -# Note that we don't install clang on Linux since its compiler story is just so -# different. Each container has its own toolchain configured appropriately -# already.
diff --git a/src/ci/azure-pipelines/steps/install-sccache.yml b/src/ci/azure-pipelines/steps/install-sccache.yml deleted file mode 100644 index d4679c1..0000000 --- a/src/ci/azure-pipelines/steps/install-sccache.yml +++ /dev/null
@@ -1,21 +0,0 @@ -steps: - -- bash: | - set -e - curl -fo /usr/local/bin/sccache https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc/2018-04-02-sccache-x86_64-apple-darwin - chmod +x /usr/local/bin/sccache - displayName: Install sccache (OSX) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin')) - -- script: | - md sccache - powershell -Command "$ProgressPreference = 'SilentlyContinue'; iwr -outf sccache\sccache.exe https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc/2018-04-26-sccache-x86_64-pc-windows-msvc" - echo ##vso[task.prependpath]%CD%\sccache - displayName: Install sccache (Windows) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - -# Note that we don't install sccache on Linux since it's installed elsewhere -# through all the containers. -# -# FIXME: we should probably install sccache outside the containers and then -# mount it inside the containers so we can centralize all installation here.
diff --git a/src/ci/azure-pipelines/steps/install-windows-build-deps.yml b/src/ci/azure-pipelines/steps/install-windows-build-deps.yml deleted file mode 100644 index bd4f1ed..0000000 --- a/src/ci/azure-pipelines/steps/install-windows-build-deps.yml +++ /dev/null
@@ -1,120 +0,0 @@ -steps: -# We use the WIX toolset to create combined installers for Windows, and these -# binaries are downloaded from -# https://github.com/wixtoolset/wix3 originally -- bash: | - set -e - curl -O https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc/wix311-binaries.zip - echo "##vso[task.setvariable variable=WIX]`pwd`/wix" - mkdir -p wix/bin - cd wix/bin - 7z x ../../wix311-binaries.zip - displayName: Install wix - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - -# We use InnoSetup and its `iscc` program to also create combined installers. -# Honestly at this point WIX above and `iscc` are just holdovers from -# oh-so-long-ago and are required for creating installers on Windows. I think -# one is MSI installers and one is EXE, but they're not used so frequently at -# this point anyway so perhaps it's a wash! -- script: | - echo ##vso[task.prependpath]C:\Program Files (x86)\Inno Setup 5 - curl.exe -o is-install.exe https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc/2017-08-22-is.exe - is-install.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- - displayName: Install InnoSetup - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - -# We've had issues with the default drive in use running out of space during a -# build, and it looks like the `C:` drive has more space than the default `D:` -# drive. We should probably confirm this with the azure pipelines team at some -# point, but this seems to fix our "disk space full" problems. -- script: | - mkdir c:\MORE_SPACE - mklink /J build c:\MORE_SPACE - displayName: "Ensure build happens on C:/ instead of D:/" - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - -- bash: git config --replace-all --global core.autocrlf false - displayName: "Disable git automatic line ending conversion (on C:/)" - -# Download and install MSYS2, needed primarily for the test suite (run-make) but -# also used by the MinGW toolchain for assembling things. -# -# FIXME: we should probe the default azure image and see if we can use the MSYS2 -# toolchain there. (if there's even one there). For now though this gets the job -# done. -- bash: | - set -e - choco install msys2 --params="/InstallDir:$(System.Workfolder)/msys2 /NoPath" -y --no-progress - echo "##vso[task.prependpath]$(System.Workfolder)/msys2/usr/bin" - mkdir -p "$(System.Workfolder)/msys2/home/$USERNAME" - displayName: Install msys2 - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - -- bash: pacman -S --noconfirm --needed base-devel ca-certificates make diffutils tar - displayName: Install msys2 base deps - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - -# If we need to download a custom MinGW, do so here and set the path -# appropriately. -# -# Here we also do a pretty heinous thing which is to mangle the MinGW -# installation we just downloaded. Currently, as of this writing, we're using -# MinGW-w64 builds of gcc, and that's currently at 6.3.0. We use 6.3.0 as it -# appears to be the first version which contains a fix for #40546, builds -# randomly failing during LLVM due to ar.exe/ranlib.exe failures. -# -# Unfortunately, though, 6.3.0 *also* is the first version of MinGW-w64 builds -# to contain a regression in gdb (#40184). As a result if we were to use the -# gdb provided (7.11.1) then we would fail all debuginfo tests. -# -# In order to fix spurious failures (pretty high priority) we use 6.3.0. To -# avoid disabling gdb tests we download an *old* version of gdb, specifically -# that found inside the 6.2.0 distribution. We then overwrite the 6.3.0 gdb -# with the 6.2.0 gdb to get tests passing. -# -# Note that we don't literally overwrite the gdb.exe binary because it appears -# to just use gdborig.exe, so that's the binary we deal with instead. -- bash: | - set -e - curl -o mingw.7z $MINGW_URL/$MINGW_ARCHIVE - 7z x -y mingw.7z > /dev/null - curl -o $MINGW_DIR/bin/gdborig.exe $MINGW_URL/2017-04-20-${MSYS_BITS}bit-gdborig.exe - echo "##vso[task.prependpath]`pwd`/$MINGW_DIR/bin" - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), ne(variables['MINGW_URL'],'')) - displayName: Download custom MinGW - -# Otherwise install MinGW through `pacman` -- bash: | - set -e - arch=i686 - if [ "$MSYS_BITS" = "64" ]; then - arch=x86_64 - fi - pacman -S --noconfirm --needed mingw-w64-$arch-toolchain mingw-w64-$arch-cmake mingw-w64-$arch-gcc mingw-w64-$arch-python2 - echo "##vso[task.prependpath]$(System.Workfolder)/msys2/mingw$MSYS_BITS/bin" - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['MINGW_URL'],'')) - displayName: Download standard MinGW - -# Make sure we use the native python interpreter instead of some msys equivalent -# one way or another. The msys interpreters seem to have weird path conversions -# baked in which break LLVM's build system one way or another, so let's use the -# native version which keeps everything as native as possible. -- bash: | - set -e - cp C:/Python27amd64/python.exe C:/Python27amd64/python2.7.exe - echo "##vso[task.prependpath]C:/Python27amd64" - displayName: Prefer the "native" Python as LLVM has trouble building with MSYS sometimes - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - -# Note that this is originally from the github releases patch of Ninja -- bash: | - set -e - mkdir ninja - curl -o ninja.zip https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc/2017-03-15-ninja-win.zip - 7z x -oninja ninja.zip - rm ninja.zip - echo "##vso[task.setvariable variable=RUST_CONFIGURE_ARGS]$RUST_CONFIGURE_ARGS --enable-ninja" - echo "##vso[task.prependpath]`pwd`/ninja" - displayName: Download and install ninja - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
diff --git a/src/ci/azure-pipelines/steps/run.yml b/src/ci/azure-pipelines/steps/run.yml index 15a2499..cef2d23 100644 --- a/src/ci/azure-pipelines/steps/run.yml +++ b/src/ci/azure-pipelines/steps/run.yml
@@ -6,11 +6,6 @@ # # Check travis config for `gdb --batch` command to print all crash logs -parameters: - # When this parameter is set to anything other than an empty string the tests - # will only be executed when the commit updates submodules - only_on_updated_submodules: '' - steps: # Disable automatic line ending conversion, which is enabled by default on @@ -26,21 +21,8 @@ - checkout: self fetchDepth: 2 -# Set the SKIP_JOB environment variable if this job is supposed to only run -# when submodules are updated and they were not. The following time consuming -# tasks will be skipped when the environment variable is present. -- ${{ if parameters.only_on_updated_submodules }}: - - bash: | - set -e - # Submodules pseudo-files inside git have the 160000 permissions, so when - # those files are present in the diff a submodule was updated. - if git diff HEAD^ | grep "^index .* 160000" >/dev/null 2>&1; then - echo "Executing the job since submodules are updated" - else - echo "Not executing this job since no submodules were updated" - echo "##vso[task.setvariable variable=SKIP_JOB;]1" - fi - displayName: Decide whether to run this job +- bash: src/ci/scripts/should-skip-this.sh + displayName: Decide whether to run this job # Spawn a background process to collect CPU usage statistics which we'll upload # at the end of the build. See the comments in the script here for more @@ -48,86 +30,106 @@ - bash: python src/ci/cpu-usage-over-time.py &> cpu-usage.csv & displayName: "Collect CPU-usage statistics in the background" -- bash: printenv | sort - displayName: Show environment variables +- bash: src/ci/scripts/dump-environment.sh + displayName: Show the current environment -- bash: | - set -e - df -h - du . | sort -nr | head -n100 - displayName: Show disk usage - # FIXME: this hasn't been tested, but maybe it works on Windows? Should test! - condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT')) +- bash: src/ci/scripts/install-sccache.sh + env: + AGENT_OS: $(Agent.OS) + displayName: Install sccache + condition: and(succeeded(), not(variables.SKIP_JOB)) -- template: install-sccache.yml -- template: install-clang.yml +- bash: src/ci/scripts/install-clang.sh + env: + AGENT_OS: $(Agent.OS) + displayName: Install clang + condition: and(succeeded(), not(variables.SKIP_JOB)) -# Switch to XCode 9.3 on OSX since it seems to be the last version that supports -# i686-apple-darwin. We'll eventually want to upgrade this and it will probably -# force us to drop i686-apple-darwin, but let's keep the wheels turning for now. -- bash: | - set -e - sudo xcode-select --switch /Applications/Xcode_9.3.app - displayName: Switch to Xcode 9.3 (OSX) - condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin')) +- bash: src/ci/scripts/switch-xcode.sh + env: + AGENT_OS: $(Agent.OS) + displayName: Switch to Xcode 9.3 + condition: and(succeeded(), not(variables.SKIP_JOB)) -- template: install-windows-build-deps.yml +- bash: src/ci/scripts/install-wix.sh + env: + AGENT_OS: $(Agent.OS) + displayName: Install wix + condition: and(succeeded(), not(variables.SKIP_JOB)) -# Looks like docker containers have IPv6 disabled by default, so let's turn it -# on since libstd tests require it -- bash: | - set -e - sudo mkdir -p /etc/docker - echo '{"ipv6":true,"fixed-cidr-v6":"fd9a:8454:6789:13f7::/64"}' | sudo tee /etc/docker/daemon.json - sudo service docker restart - displayName: Enable IPv6 - condition: and(succeeded(), not(variables.SKIP_JOB), eq(variables['Agent.OS'], 'Linux')) +- bash: src/ci/scripts/install-innosetup.sh + env: + AGENT_OS: $(Agent.OS) + displayName: Install InnoSetup + condition: and(succeeded(), not(variables.SKIP_JOB)) + +- bash: src/ci/scripts/windows-symlink-build-dir.sh + env: + AGENT_OS: $(Agent.OS) + displayName: Ensure the build happens on C:\ instead of D:\ + condition: and(succeeded(), not(variables.SKIP_JOB)) + +- bash: src/ci/scripts/disable-git-crlf-conversion.sh + displayName: "Disable git automatic line ending conversion (on C:/)" + condition: and(succeeded(), not(variables.SKIP_JOB)) + +- bash: src/ci/scripts/install-msys2.sh + env: + AGENT_OS: $(Agent.OS) + SYSTEM_WORKFOLDER: $(System.Workfolder) + displayName: Install msys2 + condition: and(succeeded(), not(variables.SKIP_JOB)) + +- bash: src/ci/scripts/install-msys2-packages.sh + env: + AGENT_OS: $(Agent.OS) + SYSTEM_WORKFOLDER: $(System.Workfolder) + displayName: Install msys2 packages + condition: and(succeeded(), not(variables.SKIP_JOB)) + +- bash: src/ci/scripts/install-mingw.sh + env: + AGENT_OS: $(Agent.OS) + SYSTEM_WORKFOLDER: $(System.Workfolder) + displayName: Install MinGW + condition: and(succeeded(), not(variables.SKIP_JOB)) + +- bash: src/ci/scripts/install-ninja.sh + env: + AGENT_OS: $(Agent.OS) + displayName: Install ninja + condition: and(succeeded(), not(variables.SKIP_JOB)) + +- bash: src/ci/scripts/enable-docker-ipv6.sh + env: + AGENT_OS: $(Agent.OS) + displayName: Enable IPv6 on Docker + condition: and(succeeded(), not(variables.SKIP_JOB)) # Disable automatic line ending conversion (again). On Windows, when we're # installing dependencies, something switches the git configuration directory or # re-enables autocrlf. We've not tracked down the exact cause -- and there may # be multiple -- but this should ensure submodules are checked out with the # appropriate line endings. -- bash: git config --replace-all --global core.autocrlf false - displayName: "Disable git automatic line ending conversion" +- bash: src/ci/scripts/disable-git-crlf-conversion.sh + displayName: Disable git automatic line ending conversion + condition: and(succeeded(), not(variables.SKIP_JOB)) -# Check out all our submodules, but more quickly than using git by using one of -# our custom scripts -- bash: | - set -e - mkdir -p $HOME/rustsrc - $BUILD_SOURCESDIRECTORY/src/ci/init_repo.sh . $HOME/rustsrc - condition: and(succeeded(), not(variables.SKIP_JOB), ne(variables['Agent.OS'], 'Windows_NT')) - displayName: Check out submodules (Unix) -- script: | - if not exist C:\cache\rustsrc\NUL mkdir C:\cache\rustsrc - sh src/ci/init_repo.sh . /c/cache/rustsrc - condition: and(succeeded(), not(variables.SKIP_JOB), eq(variables['Agent.OS'], 'Windows_NT')) - displayName: Check out submodules (Windows) +- bash: src/ci/scripts/checkout-submodules.sh + env: + AGENT_OS: $(Agent.OS) + displayName: Checkout submodules + condition: and(succeeded(), not(variables.SKIP_JOB)) -# See also the disable for autocrlf above, this just checks that it worked -# -# We check both in rust-lang/rust and in a submodule to make sure both are -# accurate. Submodules are checked out significantly later than the main -# repository in this script, so settings can (and do!) change between then. -# -# Linux (and maybe macOS) builders don't currently have dos2unix so just only -# run this step on Windows. -- bash: | - set -x - # print out the git configuration so we can better investigate failures in - # the following - git config --list --show-origin - dos2unix -ih Cargo.lock src/tools/rust-installer/install-template.sh - endings=$(dos2unix -ic Cargo.lock src/tools/rust-installer/install-template.sh) - # if endings has non-zero length, error out - if [ -n "$endings" ]; then exit 1 ; fi - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - displayName: Verify line endings are LF +- bash: src/ci/scripts/verify-line-endings.sh + env: + AGENT_OS: $(Agent.OS) + displayName: Verify line endings + condition: and(succeeded(), not(variables.SKIP_JOB)) # Ensure the `aws` CLI is installed so we can deploy later on, cache docker # images, etc. -- bash: src/ci/install-awscli.sh +- bash: src/ci/scripts/install-awscli.sh env: AGENT_OS: $(Agent.OS) condition: and(succeeded(), not(variables.SKIP_JOB)) @@ -181,37 +183,21 @@ condition: and(succeeded(), not(variables.SKIP_JOB)) displayName: Run build -# If we're a deploy builder, use the `aws` command to publish everything to our -# bucket. -- bash: | - set -e - source src/ci/shared.sh - if [ "$AGENT_OS" = "Linux" ]; then - rm -rf obj/build/dist/doc - upload_dir=obj/build/dist - else - rm -rf build/dist/doc - upload_dir=build/dist - fi - ls -la $upload_dir - deploy_dir=rustc-builds - if [ "$DEPLOY_ALT" == "1" ]; then - deploy_dir=rustc-builds-alt - fi - retry aws s3 cp --no-progress --recursive --acl public-read ./$upload_dir s3://$DEPLOY_BUCKET/$deploy_dir/$BUILD_SOURCEVERSION +- bash: src/ci/scripts/upload-artifacts.sh env: AWS_ACCESS_KEY_ID: $(UPLOAD_AWS_ACCESS_KEY_ID) AWS_SECRET_ACCESS_KEY: $(UPLOAD_AWS_SECRET_ACCESS_KEY) - condition: and(succeeded(), not(variables.SKIP_JOB), or(eq(variables.DEPLOY, '1'), eq(variables.DEPLOY_ALT, '1'))) displayName: Upload artifacts - -# Upload CPU usage statistics that we've been gathering this whole time. Always -# execute this step in case we want to inspect failed builds, but don't let -# errors here ever fail the build since this is just informational. -- bash: aws s3 cp --acl public-read cpu-usage.csv s3://$DEPLOY_BUCKET/rustc-builds/$BUILD_SOURCEVERSION/cpu-$CI_JOB_NAME.csv - env: - AWS_ACCESS_KEY_ID: $(UPLOAD_AWS_ACCESS_KEY_ID) - AWS_SECRET_ACCESS_KEY: $(UPLOAD_AWS_SECRET_ACCESS_KEY) - condition: variables['UPLOAD_AWS_SECRET_ACCESS_KEY'] - continueOnError: true - displayName: Upload CPU usage statistics + # Adding a condition on DEPLOY=1 or DEPLOY_ALT=1 is not needed as all deploy + # builders *should* have the AWS credentials available. Still, explicitly + # adding the condition is helpful as this way CI will not silently skip + # deploying artifacts from a dist builder if the variables are misconfigured, + # erroring about invalid credentials instead. + condition: | + and( + succeeded(), not(variables.SKIP_JOB), + or( + variables.UPLOAD_AWS_SECRET_ACCESS_KEY, + eq(variables.DEPLOY, '1'), eq(variables.DEPLOY_ALT, '1') + ) + )
diff --git a/src/ci/docker/asmjs/Dockerfile b/src/ci/docker/asmjs/Dockerfile deleted file mode 100644 index 3abaab6..0000000 --- a/src/ci/docker/asmjs/Dockerfile +++ /dev/null
@@ -1,47 +0,0 @@ -FROM ubuntu:16.04 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - make \ - file \ - curl \ - ca-certificates \ - python \ - git \ - cmake \ - sudo \ - gdb \ - xz-utils - -COPY scripts/emscripten.sh /scripts/ -RUN bash /scripts/emscripten.sh - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV PATH=$PATH:/emsdk-portable -ENV PATH=$PATH:/emsdk-portable/clang/e1.38.15_64bit/ -ENV PATH=$PATH:/emsdk-portable/emscripten/1.38.15/ -ENV PATH=$PATH:/emsdk-portable/node/8.9.1_64bit/bin/ -ENV EMSCRIPTEN=/emsdk-portable/emscripten/1.38.15/ -ENV BINARYEN_ROOT=/emsdk-portable/clang/e1.38.15_64bit/binaryen/ -ENV EM_CONFIG=/emsdk-portable/.emscripten - -ENV TARGETS=asmjs-unknown-emscripten - -ENV RUST_CONFIGURE_ARGS --enable-emscripten --disable-optimize-tests - -ENV SCRIPT python2.7 ../x.py test --target $TARGETS \ - src/test/ui \ - src/test/run-fail \ - src/libstd \ - src/liballoc \ - src/libcore - -# Debug assertions in rustc are largely covered by other builders, and LLVM -# assertions cause this builder to slow down by quite a large amount and don't -# buy us a huge amount over other builders (not sure if we've ever seen an -# asmjs-specific backend assertion trip), so disable assertions for these -# tests. -ENV NO_LLVM_ASSERTIONS=1 -ENV NO_DEBUG_ASSERTIONS=1
diff --git a/src/ci/docker/disabled/asmjs/Dockerfile b/src/ci/docker/disabled/asmjs/Dockerfile new file mode 100644 index 0000000..e27a2a5 --- /dev/null +++ b/src/ci/docker/disabled/asmjs/Dockerfile
@@ -0,0 +1,41 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + file \ + curl \ + ca-certificates \ + python \ + git \ + cmake \ + sudo \ + gdb \ + xz-utils \ + bzip2 + +COPY scripts/emscripten.sh /scripts/ +RUN bash /scripts/emscripten.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV PATH=$PATH:/emsdk-portable +ENV PATH=$PATH:/emsdk-portable/upstream/emscripten/ +ENV PATH=$PATH:/emsdk-portable/node/12.9.1_64bit/bin/ +ENV BINARYEN_ROOT=/emsdk-portable/upstream/ + +ENV TARGETS=asmjs-unknown-emscripten + +# Use -O1 optimizations in the link step to reduce time spent optimizing JS. +ENV EMCC_CFLAGS=-O1 + +# Emscripten installation is user-specific +ENV NO_CHANGE_USER=1 + +ENV SCRIPT python2.7 ../x.py test --target $TARGETS + +# This is almost identical to the wasm32-unknown-emscripten target, so +# running with assertions again is not useful +ENV NO_DEBUG_ASSERTIONS=1 +ENV NO_LLVM_ASSERTIONS=1
diff --git a/src/ci/docker/disabled/wasm32-exp/Dockerfile b/src/ci/docker/disabled/wasm32-exp/Dockerfile deleted file mode 100644 index 420d47b..0000000 --- a/src/ci/docker/disabled/wasm32-exp/Dockerfile +++ /dev/null
@@ -1,35 +0,0 @@ -FROM ubuntu:16.04 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - make \ - file \ - curl \ - ca-certificates \ - python \ - git \ - cmake \ - sudo \ - gdb \ - xz-utils \ - jq \ - bzip2 - -# emscripten -COPY scripts/emscripten-wasm.sh /scripts/ -COPY wasm32-exp/node.sh /usr/local/bin/node -RUN bash /scripts/emscripten-wasm.sh - -# cache -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -# env -ENV PATH=/wasm-install/emscripten:/wasm-install/bin:$PATH -ENV EM_CONFIG=/root/.emscripten - -ENV TARGETS=wasm32-experimental-emscripten - -ENV RUST_CONFIGURE_ARGS --experimental-targets=WebAssembly - -ENV SCRIPT python2.7 ../x.py test --target $TARGETS
diff --git a/src/ci/docker/disabled/wasm32-exp/node.sh b/src/ci/docker/disabled/wasm32-exp/node.sh deleted file mode 100755 index aa93897..0000000 --- a/src/ci/docker/disabled/wasm32-exp/node.sh +++ /dev/null
@@ -1,9 +0,0 @@ -#!/usr/bin/env bash - -path="$(dirname $1)" -file="$(basename $1)" - -shift - -cd "$path" -exec /node-v8.0.0-linux-x64/bin/node "$file" "$@"
diff --git a/src/ci/docker/disabled/wasm32/Dockerfile b/src/ci/docker/disabled/wasm32/Dockerfile deleted file mode 100644 index 0d2bd39..0000000 --- a/src/ci/docker/disabled/wasm32/Dockerfile +++ /dev/null
@@ -1,32 +0,0 @@ -FROM ubuntu:16.04 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - make \ - file \ - curl \ - ca-certificates \ - python \ - git \ - cmake \ - sudo \ - gdb \ - xz-utils - -# emscripten -COPY scripts/emscripten.sh /scripts/ -RUN bash /scripts/emscripten.sh - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV PATH=$PATH:/emsdk-portable -ENV PATH=$PATH:/emsdk-portable/clang/e1.38.15_64bit/ -ENV PATH=$PATH:/emsdk-portable/emscripten/1.38.15/ -ENV PATH=$PATH:/emsdk-portable/node/8.9.1_64bit/bin/ -ENV EMSCRIPTEN=/emsdk-portable/emscripten/1.38.15/ -ENV BINARYEN_ROOT=/emsdk-portable/clang/e1.38.15_64bit/binaryen/ -ENV EM_CONFIG=/emsdk-portable/.emscripten - -ENV TARGETS=wasm32-unknown-emscripten -ENV SCRIPT python2.7 ../x.py test --target $TARGETS
diff --git a/src/ci/docker/dist-various-1/Dockerfile b/src/ci/docker/dist-various-1/Dockerfile index 1057911..816bdb3 100644 --- a/src/ci/docker/dist-various-1/Dockerfile +++ b/src/ci/docker/dist-various-1/Dockerfile
@@ -15,6 +15,8 @@ g++-arm-linux-gnueabi \ g++-arm-linux-gnueabihf \ g++-aarch64-linux-gnu \ + g++-mips64-linux-gnuabi64 \ + g++-mips64el-linux-gnuabi64 \ gcc-sparc64-linux-gnu \ libc6-dev-sparc64-cross \ bzip2 \ @@ -77,6 +79,14 @@ CC=mipsel-openwrt-linux-gcc \ CXX=mipsel-openwrt-linux-g++ \ bash musl.sh mipsel && \ + env \ + CC=mips64-linux-gnuabi64-gcc \ + CXX=mips64-linux-gnuabi64-g++ \ + bash musl.sh mips64 && \ + env \ + CC=mips64el-linux-gnuabi64-gcc \ + CXX=mips64el-linux-gnuabi64-g++ \ + bash musl.sh mips64el && \ rm -rf /build/* # FIXME(mozilla/sccache#235) this shouldn't be necessary but is currently @@ -97,6 +107,8 @@ ENV TARGETS=$TARGETS,x86_64-rumprun-netbsd ENV TARGETS=$TARGETS,mips-unknown-linux-musl ENV TARGETS=$TARGETS,mipsel-unknown-linux-musl +ENV TARGETS=$TARGETS,mips64-unknown-linux-muslabi64 +ENV TARGETS=$TARGETS,mips64el-unknown-linux-muslabi64 ENV TARGETS=$TARGETS,arm-unknown-linux-musleabi ENV TARGETS=$TARGETS,arm-unknown-linux-musleabihf ENV TARGETS=$TARGETS,armv5te-unknown-linux-gnueabi @@ -125,6 +137,8 @@ ENV CC_mipsel_unknown_linux_musl=mipsel-openwrt-linux-gcc \ CC_mips_unknown_linux_musl=mips-openwrt-linux-gcc \ + CC_mips64el_unknown_linux_muslabi64=mips64el-linux-gnuabi64-gcc \ + CC_mips64_unknown_linux_muslabi64=mips64-linux-gnuabi64-gcc \ CC_sparc64_unknown_linux_gnu=sparc64-linux-gnu-gcc \ CC_x86_64_unknown_redox=x86_64-unknown-redox-gcc \ CC_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc \ @@ -139,7 +153,8 @@ --musl-root-aarch64=/musl-aarch64 \ --musl-root-mips=/musl-mips \ --musl-root-mipsel=/musl-mipsel \ - --enable-emscripten \ + --musl-root-mips64=/musl-mips64 \ + --musl-root-mips64el=/musl-mips64el \ --disable-docs ENV SCRIPT \
diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 415d6b6..cdafcba 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh
@@ -106,6 +106,7 @@ mkdir -p $HOME/.cargo mkdir -p $objdir/tmp mkdir -p $objdir/cores +mkdir -p /tmp/toolstate args= if [ "$SCCACHE_BUCKET" != "" ]; then @@ -156,6 +157,7 @@ args="$args --volume $objdir:/checkout/obj" args="$args --volume $HOME/.cargo:/cargo" args="$args --volume $HOME/rustsrc:$HOME/rustsrc" + args="$args --volume /tmp/toolstate:/tmp/toolstate" args="$args --env LOCAL_USER_ID=`id -u`" fi
diff --git a/src/ci/docker/scripts/emscripten-wasm.sh b/src/ci/docker/scripts/emscripten-wasm.sh deleted file mode 100644 index e4a93d7..0000000 --- a/src/ci/docker/scripts/emscripten-wasm.sh +++ /dev/null
@@ -1,37 +0,0 @@ -set -ex - -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - $@ &> /tmp/build.log - trap - ERR - kill $PING_LOOP_PID - rm -f /tmp/build.log - set -x -} - -# Download last known good emscripten from WebAssembly waterfall -BUILD=$(curl -fL https://storage.googleapis.com/wasm-llvm/builds/linux/lkgr.json | \ - jq '.build | tonumber') -curl -sL https://storage.googleapis.com/wasm-llvm/builds/linux/$BUILD/wasm-binaries.tbz2 | \ - hide_output tar xvkj - -# node 8 is required to run wasm -cd / -curl -sL https://nodejs.org/dist/v8.0.0/node-v8.0.0-linux-x64.tar.xz | \ - tar -xJ - -# Make emscripten use wasm-ready node and LLVM tools -echo "EMSCRIPTEN_ROOT = '/wasm-install/emscripten'" >> /root/.emscripten -echo "NODE_JS='/usr/local/bin/node'" >> /root/.emscripten -echo "LLVM_ROOT='/wasm-install/bin'" >> /root/.emscripten -echo "BINARYEN_ROOT = '/wasm-install'" >> /root/.emscripten -echo "COMPILER_ENGINE = NODE_JS" >> /root/.emscripten -echo "JS_ENGINES = [NODE_JS]" >> /root/.emscripten
diff --git a/src/ci/docker/scripts/emscripten.sh b/src/ci/docker/scripts/emscripten.sh index 47196e8..1be8074 100644 --- a/src/ci/docker/scripts/emscripten.sh +++ b/src/ci/docker/scripts/emscripten.sh
@@ -17,22 +17,7 @@ set -x } -cd / -curl -fL https://mozilla-games.s3.amazonaws.com/emscripten/releases/emsdk-portable.tar.gz | \ - tar -xz - +git clone https://github.com/emscripten-core/emsdk.git /emsdk-portable cd /emsdk-portable -./emsdk update -hide_output ./emsdk install sdk-1.38.15-64bit -./emsdk activate sdk-1.38.15-64bit - -# Compile and cache libc -source ./emsdk_env.sh -echo "main(){}" > a.c -HOME=/emsdk-portable/ emcc a.c -HOME=/emsdk-portable/ emcc -s BINARYEN=1 a.c -rm -f a.* - -# Make emsdk usable by any user -cp /root/.emscripten /emsdk-portable -chmod a+rxw -R /emsdk-portable +hide_output ./emsdk install 1.38.46-upstream +./emsdk activate 1.38.46-upstream
diff --git a/src/ci/docker/wasm32/Dockerfile b/src/ci/docker/wasm32/Dockerfile new file mode 100644 index 0000000..a0f35af --- /dev/null +++ b/src/ci/docker/wasm32/Dockerfile
@@ -0,0 +1,44 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + file \ + curl \ + ca-certificates \ + python \ + git \ + cmake \ + sudo \ + gdb \ + xz-utils \ + bzip2 + +COPY scripts/emscripten.sh /scripts/ +RUN bash /scripts/emscripten.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV PATH=$PATH:/emsdk-portable +ENV PATH=$PATH:/emsdk-portable/upstream/emscripten/ +ENV PATH=$PATH:/emsdk-portable/node/12.9.1_64bit/bin/ +ENV BINARYEN_ROOT=/emsdk-portable/upstream/ + +ENV TARGETS=wasm32-unknown-emscripten + +# Use -O1 optimizations in the link step to reduce time spent optimizing. +ENV EMCC_CFLAGS=-O1 + +# Emscripten installation is user-specific +ENV NO_CHANGE_USER=1 + +# FIXME: Re-enable these tests once https://github.com/rust-lang/cargo/pull/7476 +# is picked up by CI +ENV SCRIPT python2.7 ../x.py test --target $TARGETS \ + --exclude src/libcore \ + --exclude src/liballoc \ + --exclude src/libproc_macro \ + --exclude src/libstd \ + --exclude src/libterm \ + --exclude src/libtest
diff --git a/src/ci/docker/x86_64-gnu-tools/Dockerfile b/src/ci/docker/x86_64-gnu-tools/Dockerfile index 8035195..7687a6c 100644 --- a/src/ci/docker/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/x86_64-gnu-tools/Dockerfile
@@ -26,5 +26,5 @@ ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ - --save-toolstates=/tmp/toolstates.json -ENV SCRIPT /tmp/checktools.sh ../x.py /tmp/toolstates.json linux + --save-toolstates=/tmp/toolstate/toolstates.json +ENV SCRIPT /tmp/checktools.sh ../x.py /tmp/toolstate/toolstates.json linux
diff --git a/src/ci/docker/x86_64-gnu-tools/checktools.sh b/src/ci/docker/x86_64-gnu-tools/checktools.sh index 4243eff..ebb8c0b 100755 --- a/src/ci/docker/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/x86_64-gnu-tools/checktools.sh
@@ -3,7 +3,7 @@ set -eu X_PY="$1" -TOOLSTATE_FILE="$(realpath $2)" +TOOLSTATE_FILE="$(realpath -m $2)" OS="$3" COMMIT="$(git rev-parse HEAD)" CHANGED_FILES="$(git diff --name-status HEAD HEAD^)" @@ -13,6 +13,7 @@ # The Wednesday after this has value 0. # We track this value to prevent regressing tools in the last week of the 6-week cycle. +mkdir -p "$(dirname $TOOLSTATE_FILE)" touch "$TOOLSTATE_FILE" # Try to test all the tools and store the build/test success in the TOOLSTATE_FILE
diff --git a/src/ci/init_repo.sh b/src/ci/init_repo.sh index c7c3b0a..92c6e54 100755 --- a/src/ci/init_repo.sh +++ b/src/ci/init_repo.sh
@@ -47,7 +47,7 @@ rm $cached } -included="src/llvm-project src/llvm-emscripten src/doc/book src/doc/rust-by-example" +included="src/llvm-project src/doc/book src/doc/rust-by-example" modules="$(git config --file .gitmodules --get-regexp '\.path$' | cut -d' ' -f2)" modules=($modules) use_git=""
diff --git a/src/ci/install-awscli.sh b/src/ci/install-awscli.sh deleted file mode 100755 index 69c8d2e..0000000 --- a/src/ci/install-awscli.sh +++ /dev/null
@@ -1,35 +0,0 @@ -#!/bin/bash -# This script downloads and installs awscli from the packages mirrored in our -# own S3 bucket. This follows the recommendations at: -# -# https://packaging.python.org/guides/index-mirrors-and-caches/#caching-with-pip -# -# To create a new mirrored copy you can run the command: -# -# pip wheel awscli -# -# Before compressing please make sure all the wheels end with `-none-any.whl`. -# If that's not the case you'll need to remove the non-cross-platform ones and -# replace them with the .tar.gz downloaded from https://pypi.org. Also make -# sure it's possible to call this script with both Python 2 and Python 3. - -set -euo pipefail -IFS=$'\n\t' - -MIRROR="https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc/2019-07-27-awscli.tar" -DEPS_DIR="/tmp/awscli-deps" - -pip="pip" -pipflags="" -if [[ "${AGENT_OS}" == "Linux" ]]; then - pip="pip3" - pipflags="--user" - - sudo apt-get install -y python3-setuptools - echo "##vso[task.prependpath]$HOME/.local/bin" -fi - -mkdir -p "${DEPS_DIR}" -curl "${MIRROR}" | tar xf - -C "${DEPS_DIR}" -"${pip}" install ${pipflags} --no-index "--find-links=${DEPS_DIR}" awscli -rm -rf "${DEPS_DIR}"
diff --git a/src/ci/run.sh b/src/ci/run.sh index c3f8e4c..7c5df63 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh
@@ -55,6 +55,9 @@ if [ "$NO_LLVM_ASSERTIONS" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions" elif [ "$DEPLOY_ALT" != "" ]; then + if [ "$NO_PARALLEL_COMPILER" = "" ]; then + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.parallel-compiler" + fi RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-assertions" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.verify-llvm-ir" fi @@ -114,7 +117,7 @@ # Display the CPU and memory information. This helps us know why the CI timing # is fluctuating. -if isOSX; then +if isMacOS; then system_profiler SPHardwareDataType || true sysctl hw || true ncpus=$(sysctl -n hw.ncpu)
diff --git a/src/ci/scripts/checkout-submodules.sh b/src/ci/scripts/checkout-submodules.sh new file mode 100755 index 0000000..0b44ea3 --- /dev/null +++ b/src/ci/scripts/checkout-submodules.sh
@@ -0,0 +1,17 @@ +#!/bin/bash +# Check out all our submodules, but more quickly than using git by using one of +# our custom scripts + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isWindows; then + path="/c/cache/rustsrc" +else + path="${HOME}/rustsrc" +fi + +mkdir -p "${path}" +"$(cd "$(dirname "$0")" && pwd)/../init_repo.sh" . "${path}"
diff --git a/src/ci/scripts/disable-git-crlf-conversion.sh b/src/ci/scripts/disable-git-crlf-conversion.sh new file mode 100755 index 0000000..836145f --- /dev/null +++ b/src/ci/scripts/disable-git-crlf-conversion.sh
@@ -0,0 +1,13 @@ +#!/bin/bash +# Disable automatic line ending conversion, which is enabled by default on +# Azure's Windows image. Having the conversion enabled caused regressions both +# in our test suite (it broke miri tests) and in the ecosystem, since we +# started shipping install scripts with CRLF endings instead of the old LF. +# +# Note that we do this a couple times during the build as the PATH and current +# user/directory change, e.g. when mingw is enabled. + +set -euo pipefail +IFS=$'\n\t' + +git config --replace-all --global core.autocrlf false
diff --git a/src/ci/scripts/dump-environment.sh b/src/ci/scripts/dump-environment.sh new file mode 100755 index 0000000..c6774b5 --- /dev/null +++ b/src/ci/scripts/dump-environment.sh
@@ -0,0 +1,19 @@ +#!/bin/bash +# This script dumps information about the build environment to stdout. + +set -euo pipefail +IFS=$'\n\t' + +echo "environment variables:" +printenv | sort +echo + +echo "disk usage:" +df -h +echo + +echo "biggest files in the working dir:" +set +o pipefail +du . | sort -nr | head -n100 +set -o pipefail +echo
diff --git a/src/ci/scripts/enable-docker-ipv6.sh b/src/ci/scripts/enable-docker-ipv6.sh new file mode 100755 index 0000000..03d5a75 --- /dev/null +++ b/src/ci/scripts/enable-docker-ipv6.sh
@@ -0,0 +1,15 @@ +#!/bin/bash +# Looks like docker containers have IPv6 disabled by default, so let's turn it +# on since libstd tests require it + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isLinux; then + sudo mkdir -p /etc/docker + echo '{"ipv6":true,"fixed-cidr-v6":"fd9a:8454:6789:13f7::/64"}' \ + | sudo tee /etc/docker/daemon.json + sudo service docker restart +fi
diff --git a/src/ci/scripts/install-awscli.sh b/src/ci/scripts/install-awscli.sh new file mode 100755 index 0000000..e211879 --- /dev/null +++ b/src/ci/scripts/install-awscli.sh
@@ -0,0 +1,37 @@ +#!/bin/bash +# This script downloads and installs awscli from the packages mirrored in our +# own S3 bucket. This follows the recommendations at: +# +# https://packaging.python.org/guides/index-mirrors-and-caches/#caching-with-pip +# +# To create a new mirrored copy you can run the command: +# +# pip wheel awscli +# +# Before compressing please make sure all the wheels end with `-none-any.whl`. +# If that's not the case you'll need to remove the non-cross-platform ones and +# replace them with the .tar.gz downloaded from https://pypi.org. Also make +# sure it's possible to call this script with both Python 2 and Python 3. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +MIRROR="${MIRRORS_BASE}/2019-07-27-awscli.tar" +DEPS_DIR="/tmp/awscli-deps" + +pip="pip" +pipflags="" +if isLinux; then + pip="pip3" + pipflags="--user" + + sudo apt-get install -y python3-setuptools + echo "##vso[task.prependpath]$HOME/.local/bin" +fi + +mkdir -p "${DEPS_DIR}" +curl "${MIRROR}" | tar xf - -C "${DEPS_DIR}" +"${pip}" install ${pipflags} --no-index "--find-links=${DEPS_DIR}" awscli +rm -rf "${DEPS_DIR}"
diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh new file mode 100755 index 0000000..e9b6857 --- /dev/null +++ b/src/ci/scripts/install-clang.sh
@@ -0,0 +1,43 @@ +#!/bin/bash +# This script installs clang on the local machine. Note that we don't install +# clang on Linux since its compiler story is just so different. Each container +# has its own toolchain configured appropriately already. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isMacOS; then + curl -f "${MIRRORS_BASE}/clang%2Bllvm-7.0.0-x86_64-apple-darwin.tar.xz" | tar xJf - + + ciCommandSetEnv CC "$(pwd)/clang+llvm-7.0.0-x86_64-apple-darwin/bin/clang" + ciCommandSetEnv CXX "$(pwd)/clang+llvm-7.0.0-x86_64-apple-darwin/bin/clang++" + + # Configure `AR` specifically so rustbuild doesn't try to infer it as + # `clang-ar` by accident. + ciCommandSetEnv AR "ar" +elif isWindows && [[ -z ${MINGW_URL+x} ]]; then + # If we're compiling for MSVC then we, like most other distribution builders, + # switch to clang as the compiler. This'll allow us eventually to enable LTO + # amongst LLVM and rustc. Note that we only do this on MSVC as I don't think + # clang has an output mode compatible with MinGW that we need. If it does we + # should switch to clang for MinGW as well! + # + # Note that the LLVM installer is an NSIS installer + # + # Original downloaded here came from + # http://releases.llvm.org/7.0.0/LLVM-7.0.0-win64.exe + # That installer was run through `wine` on Linux and then the resulting + # installation directory (found in `$HOME/.wine/drive_c/Program Files/LLVM`) was + # packaged up into a tarball. We've had issues otherwise that the installer will + # randomly hang, provide not a lot of useful information, pollute global state, + # etc. In general the tarball is just more confined and easier to deal with when + # working with various CI environments. + + mkdir -p citools + cd citools + curl -f "${MIRRORS_BASE}/LLVM-7.0.0-win64.tar.gz" | tar xzf - + ciCommandSetEnv RUST_CONFIGURE_ARGS \ + "${RUST_CONFIGURE_ARGS} --set llvm.clang-cl=$(pwd)/clang-rust/bin/clang-cl.exe" +fi
diff --git a/src/ci/scripts/install-innosetup.sh b/src/ci/scripts/install-innosetup.sh new file mode 100755 index 0000000..04ca249 --- /dev/null +++ b/src/ci/scripts/install-innosetup.sh
@@ -0,0 +1,18 @@ +#!/bin/bash +# We use InnoSetup and its `iscc` program to also create combined installers. +# Honestly at this point WIX above and `iscc` are just holdovers from +# oh-so-long-ago and are required for creating installers on Windows. I think +# one is MSI installers and one is EXE, but they're not used so frequently at +# this point anyway so perhaps it's a wash! + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isWindows; then + curl.exe -o is-install.exe "${MIRRORS_BASE}/2017-08-22-is.exe" + cmd.exe //c "is-install.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-" + + ciCommandAddPath "C:\\Program Files (x86)\\Inno Setup 5" +fi
diff --git a/src/ci/scripts/install-mingw.sh b/src/ci/scripts/install-mingw.sh new file mode 100755 index 0000000..b4e8b88 --- /dev/null +++ b/src/ci/scripts/install-mingw.sh
@@ -0,0 +1,45 @@ +#!/bin/bash +# If we need to download a custom MinGW, do so here and set the path +# appropriately. +# +# Here we also do a pretty heinous thing which is to mangle the MinGW +# installation we just downloaded. Currently, as of this writing, we're using +# MinGW-w64 builds of gcc, and that's currently at 6.3.0. We use 6.3.0 as it +# appears to be the first version which contains a fix for #40546, builds +# randomly failing during LLVM due to ar.exe/ranlib.exe failures. +# +# Unfortunately, though, 6.3.0 *also* is the first version of MinGW-w64 builds +# to contain a regression in gdb (#40184). As a result if we were to use the +# gdb provided (7.11.1) then we would fail all debuginfo tests. +# +# In order to fix spurious failures (pretty high priority) we use 6.3.0. To +# avoid disabling gdb tests we download an *old* version of gdb, specifically +# that found inside the 6.2.0 distribution. We then overwrite the 6.3.0 gdb +# with the 6.2.0 gdb to get tests passing. +# +# Note that we don't literally overwrite the gdb.exe binary because it appears +# to just use gdborig.exe, so that's the binary we deal with instead. +# +# Otherwise install MinGW through `pacman` + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isWindows; then + if [[ -z "${MINGW_URL+x}" ]]; then + arch=i686 + if [ "$MSYS_BITS" = "64" ]; then + arch=x86_64 + fi + pacman -S --noconfirm --needed mingw-w64-$arch-toolchain mingw-w64-$arch-cmake \ + mingw-w64-$arch-gcc mingw-w64-$arch-python2 + ciCommandAddPath "${SYSTEM_WORKFOLDER}/msys2/mingw${MSYS_BITS}/bin" + else + curl -o mingw.7z "${MINGW_URL}/${MINGW_ARCHIVE}" + 7z x -y mingw.7z > /dev/null + curl -o "${MINGW_DIR}/bin/gdborig.exe" "${MINGW_URL}/2017-04-20-${MSYS_BITS}bit-gdborig.exe" + ciCommandAddPath "$(pwd)/${MINGW_DIR}/bin" + fi +fi
diff --git a/src/ci/scripts/install-msys2-packages.sh b/src/ci/scripts/install-msys2-packages.sh new file mode 100755 index 0000000..36d9202 --- /dev/null +++ b/src/ci/scripts/install-msys2-packages.sh
@@ -0,0 +1,17 @@ +#!/bin/bash + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isWindows; then + pacman -S --noconfirm --needed base-devel ca-certificates make diffutils tar + + # Make sure we use the native python interpreter instead of some msys equivalent + # one way or another. The msys interpreters seem to have weird path conversions + # baked in which break LLVM's build system one way or another, so let's use the + # native version which keeps everything as native as possible. + cp C:/Python27amd64/python.exe C:/Python27amd64/python2.7.exe + ciCommandAddPath "C:\\Python27amd64" +fi
diff --git a/src/ci/scripts/install-msys2.sh b/src/ci/scripts/install-msys2.sh new file mode 100755 index 0000000..ce37c3b --- /dev/null +++ b/src/ci/scripts/install-msys2.sh
@@ -0,0 +1,19 @@ +#!/bin/bash +# Download and install MSYS2, needed primarily for the test suite (run-make) but +# also used by the MinGW toolchain for assembling things. +# +# FIXME: we should probe the default azure image and see if we can use the MSYS2 +# toolchain there. (if there's even one there). For now though this gets the job +# done. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isWindows; then + choco install msys2 --params="/InstallDir:${SYSTEM_WORKFOLDER}/msys2 /NoPath" -y --no-progress + mkdir -p "${SYSTEM_WORKFOLDER}/msys2/home/${USERNAME}" + + ciCommandAddPath "${SYSTEM_WORKFOLDER}/msys2/usr/bin" +fi
diff --git a/src/ci/scripts/install-ninja.sh b/src/ci/scripts/install-ninja.sh new file mode 100755 index 0000000..b8261d8 --- /dev/null +++ b/src/ci/scripts/install-ninja.sh
@@ -0,0 +1,16 @@ +#!/bin/bash +# Note that this is originally from the github releases patch of Ninja + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isWindows; then + mkdir ninja + curl -o ninja.zip "${MIRRORS_BASE}/2017-03-15-ninja-win.zip" + 7z x -oninja ninja.zip + rm ninja.zip + ciCommandSetEnv "RUST_CONFIGURE_ARGS" "${RUST_CONFIGURE_ARGS} --enable-ninja" + ciCommandAddPath "$(pwd)/ninja" +fi
diff --git a/src/ci/scripts/install-sccache.sh b/src/ci/scripts/install-sccache.sh new file mode 100755 index 0000000..d3c2989 --- /dev/null +++ b/src/ci/scripts/install-sccache.sh
@@ -0,0 +1,20 @@ +#!/bin/bash +# This script installs sccache on the local machine. Note that we don't install +# sccache on Linux since it's installed elsewhere through all the containers. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isMacOS; then + curl -fo /usr/local/bin/sccache "${MIRRORS_BASE}/2018-04-02-sccache-x86_64-apple-darwin" + chmod +x /usr/local/bin/sccache +elif isWindows; then + mkdir -p sccache + curl -fo sccache/sccache.exe "${MIRRORS_BASE}/2018-04-26-sccache-x86_64-pc-windows-msvc" + ciCommandAddPath "$(pwd)/sccache" +fi + +# FIXME: we should probably install sccache outside the containers and then +# mount it inside the containers so we can centralize all installation here.
diff --git a/src/ci/scripts/install-wix.sh b/src/ci/scripts/install-wix.sh new file mode 100755 index 0000000..688f1a4 --- /dev/null +++ b/src/ci/scripts/install-wix.sh
@@ -0,0 +1,17 @@ +#!/bin/bash +# We use the WIX toolset to create combined installers for Windows, and these +# binaries are downloaded from https://github.com/wixtoolset/wix3 originally + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isWindows; then + ciCommandSetEnv WIX "$(pwd)/wix" + + curl -O "${MIRRORS_BASE}/wix311-binaries.zip" + mkdir -p wix/bin + cd wix/bin + 7z x ../../wix311-binaries.zip +fi
diff --git a/src/ci/scripts/should-skip-this.sh b/src/ci/scripts/should-skip-this.sh new file mode 100755 index 0000000..f945db0 --- /dev/null +++ b/src/ci/scripts/should-skip-this.sh
@@ -0,0 +1,20 @@ +#!/bin/bash +# Set the SKIP_JOB environment variable if this job is supposed to only run +# when submodules are updated and they were not. The following time consuming +# tasks will be skipped when the environment variable is present. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if [[ -z "${CI_ONLY_WHEN_SUBMODULES_CHANGED+x}" ]]; then + echo "Executing the job since there is no skip rule in effect" +elif git diff HEAD^ | grep --quiet "^index .* 160000"; then + # Submodules pseudo-files inside git have the 160000 permissions, so when + # those files are present in the diff a submodule was updated. + echo "Executing the job since submodules are updated" +else + echo "Not executing this job since no submodules were updated" + ciCommandSetEnv SKIP_JOB 1 +fi
diff --git a/src/ci/scripts/switch-xcode.sh b/src/ci/scripts/switch-xcode.sh new file mode 100755 index 0000000..2cbb2dd --- /dev/null +++ b/src/ci/scripts/switch-xcode.sh
@@ -0,0 +1,13 @@ +#!/bin/bash +# Switch to XCode 9.3 on OSX since it seems to be the last version that supports +# i686-apple-darwin. We'll eventually want to upgrade this and it will probably +# force us to drop i686-apple-darwin, but let's keep the wheels turning for now. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isMacOS; then + sudo xcode-select --switch /Applications/Xcode_9.3.app +fi
diff --git a/src/ci/scripts/upload-artifacts.sh b/src/ci/scripts/upload-artifacts.sh new file mode 100755 index 0000000..312ec9d --- /dev/null +++ b/src/ci/scripts/upload-artifacts.sh
@@ -0,0 +1,41 @@ +#!/bin/bash +# Upload all the artifacts to our S3 bucket. All the files inside ${upload_dir} +# will be uploaded to the deploy bucket and eventually signed and released in +# static.rust-lang.org. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +upload_dir="$(mktemp -d)" + +# Release tarballs produced by a dist builder. +if [[ "${DEPLOY-0}" -eq "1" ]] || [[ "${DEPLOY_ALT-0}" -eq "1" ]]; then + dist_dir=build/dist + if isLinux; then + dist_dir=obj/build/dist + fi + rm -rf "${dist_dir}/doc" + cp -r "${dist_dir}"/* "${upload_dir}" +fi + +# CPU usage statistics. +cp cpu-usage.csv "${upload_dir}/cpu-${CI_JOB_NAME}.csv" + +# Toolstate data. +if [[ -n "${DEPLOY_TOOLSTATES_JSON+x}" ]]; then + cp /tmp/toolstate/toolstates.json "${upload_dir}/${DEPLOY_TOOLSTATES_JSON}" +fi + +echo "Files that will be uploaded:" +ls -lah "${upload_dir}" +echo + +deploy_dir="rustc-builds" +if [[ "${DEPLOY_ALT-0}" -eq "1" ]]; then + deploy_dir="rustc-builds-alt" +fi +deploy_url="s3://${DEPLOY_BUCKET}/${deploy_dir}/$(ciCommit)" + +retry aws s3 cp --no-progress --recursive --acl public-read "${upload_dir}" "${deploy_url}"
diff --git a/src/ci/scripts/verify-line-endings.sh b/src/ci/scripts/verify-line-endings.sh new file mode 100755 index 0000000..f3cac13 --- /dev/null +++ b/src/ci/scripts/verify-line-endings.sh
@@ -0,0 +1,24 @@ +#!/bin/bash +# See also the disable for autocrlf, this just checks that it worked. +# +# We check both in rust-lang/rust and in a submodule to make sure both are +# accurate. Submodules are checked out significantly later than the main +# repository in this script, so settings can (and do!) change between then. +# +# Linux (and maybe macOS) builders don't currently have dos2unix so just only +# run this step on Windows. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isWindows; then + # print out the git configuration so we can better investigate failures in + # the following + git config --list --show-origin + dos2unix -ih Cargo.lock src/tools/rust-installer/install-template.sh + endings=$(dos2unix -ic Cargo.lock src/tools/rust-installer/install-template.sh) + # if endings has non-zero length, error out + if [ -n "$endings" ]; then exit 1 ; fi +fi
diff --git a/src/ci/scripts/windows-symlink-build-dir.sh b/src/ci/scripts/windows-symlink-build-dir.sh new file mode 100755 index 0000000..e57128c --- /dev/null +++ b/src/ci/scripts/windows-symlink-build-dir.sh
@@ -0,0 +1,15 @@ +#!/bin/bash +# We've had issues with the default drive in use running out of space during a +# build, and it looks like the `C:` drive has more space than the default `D:` +# drive. We should probably confirm this with the azure pipelines team at some +# point, but this seems to fix our "disk space full" problems. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isWindows; then + cmd //c "mkdir c:\\MORE_SPACE" + cmd //c "mklink /J build c:\\MORE_SPACE" +fi
diff --git a/src/ci/shared.sh b/src/ci/shared.sh index b093a07..718a537 100644 --- a/src/ci/shared.sh +++ b/src/ci/shared.sh
@@ -4,6 +4,8 @@ # `source shared.sh`, hence the invalid shebang and not being # marked as an executable file in git. +export MIRRORS_BASE="https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc" + # See http://unix.stackexchange.com/questions/82598 # Duplicated in docker/dist-various-2/shared.sh function retry { @@ -28,10 +30,43 @@ [ "$CI" = "true" ] || [ "$TF_BUILD" = "True" ] } -function isOSX { +function isMacOS { [ "$AGENT_OS" = "Darwin" ] } +function isWindows { + [ "$AGENT_OS" = "Windows_NT" ] +} + +function isLinux { + [ "$AGENT_OS" = "Linux" ] +} + function getCIBranch { echo "$BUILD_SOURCEBRANCHNAME" } + +function ciCommit { + echo "${BUILD_SOURCEVERSION}" +} + +function ciCommandAddPath { + if [[ $# -ne 1 ]]; then + echo "usage: $0 <path>" + exit 1 + fi + path="$1" + + echo "##vso[task.prependpath]${path}" +} + +function ciCommandSetEnv { + if [[ $# -ne 2 ]]; then + echo "usage: $0 <name> <value>" + exit 1 + fi + name="$1" + value="$2" + + echo "##vso[task.setvariable variable=${name}]${value}" +}
diff --git a/src/doc/book/Cargo.lock b/src/doc/book/Cargo.lock index 928f6b1..dbf9557 100644 --- a/src/doc/book/Cargo.lock +++ b/src/doc/book/Cargo.lock
@@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "aho-corasick" version = "0.5.3"
diff --git a/src/doc/book/ci/build.sh b/src/doc/book/ci/build.sh old mode 100644 new mode 100755 index 9ad1781..13f4509 --- a/src/doc/book/ci/build.sh +++ b/src/doc/book/ci/build.sh
@@ -4,13 +4,6 @@ export PATH=$PATH:/home/travis/.cargo/bin; -# Feature check -cd ci/stable-check - -cargo run -- ../../src - -cd ../.. - echo 'Spellchecking...' bash ci/spellcheck.sh list echo 'Testing...' @@ -19,3 +12,8 @@ mdbook build echo 'Linting for local file paths...' cargo run --bin lfp src +echo 'Validating references' +for file in src/*.md ; do + echo Checking references in $file + cargo run --quiet --bin link2print < $file > /dev/null +done
diff --git a/src/doc/book/ci/stable-check/Cargo.lock b/src/doc/book/ci/stable-check/Cargo.lock deleted file mode 100644 index 9a3b307..0000000 --- a/src/doc/book/ci/stable-check/Cargo.lock +++ /dev/null
@@ -1,4 +0,0 @@ -[root] -name = "stable-check" -version = "0.1.0" -
diff --git a/src/doc/book/ci/stable-check/Cargo.toml b/src/doc/book/ci/stable-check/Cargo.toml deleted file mode 100644 index 691722a..0000000 --- a/src/doc/book/ci/stable-check/Cargo.toml +++ /dev/null
@@ -1,6 +0,0 @@ -[package] -name = "stable-check" -version = "0.1.0" -authors = ["steveklabnik <steve@steveklabnik.com>"] - -[dependencies]
diff --git a/src/doc/book/ci/stable-check/src/main.rs b/src/doc/book/ci/stable-check/src/main.rs deleted file mode 100644 index 167f1f8..0000000 --- a/src/doc/book/ci/stable-check/src/main.rs +++ /dev/null
@@ -1,43 +0,0 @@ -use std::error::Error; -use std::env; -use std::fs; -use std::fs::File; -use std::io::prelude::*; -use std::path::Path; - -fn main() { - let arg = env::args().nth(1).unwrap_or_else(|| { - println!("Please pass a src directory as the first argument"); - std::process::exit(1); - }); - - match check_directory(&Path::new(&arg)) { - Ok(()) => println!("passed!"), - Err(e) => { - println!("Error: {}", e); - std::process::exit(1); - } - } - -} - -fn check_directory(dir: &Path) -> Result<(), Box<Error>> { - for entry in fs::read_dir(dir)? { - let entry = entry?; - let path = entry.path(); - - if path.is_dir() { - continue; - } - - let mut file = File::open(&path)?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - - if contents.contains("#![feature") { - return Err(From::from(format!("Feature flag found in {:?}", path))); - } - } - - Ok(()) -}
diff --git a/src/doc/book/rust-toolchain b/src/doc/book/rust-toolchain new file mode 100644 index 0000000..bf50e91 --- /dev/null +++ b/src/doc/book/rust-toolchain
@@ -0,0 +1 @@ +1.37.0
diff --git a/src/doc/book/src/appendix-04-useful-development-tools.md b/src/doc/book/src/appendix-04-useful-development-tools.md index ec40d1c..33929bc 100644 --- a/src/doc/book/src/appendix-04-useful-development-tools.md +++ b/src/doc/book/src/appendix-04-useful-development-tools.md
@@ -1,10 +1,10 @@ -# Appendix D - Useful Development Tools +## Appendix D - Useful Development Tools In this appendix, we talk about some useful development tools that the Rust project provides. We’ll look at automatic formatting, quick ways to apply warning fixes, a linter, and integrating with IDEs. -## Automatic Formatting with `rustfmt` +### Automatic Formatting with `rustfmt` The `rustfmt` tool reformats your code according to the community code style. Many collaborative projects use `rustfmt` to prevent arguments about which @@ -29,7 +29,7 @@ [rustfmt]: https://github.com/rust-lang/rustfmt -## Fix Your Code with `rustfix` +### Fix Your Code with `rustfix` The rustfix tool is included with Rust installations and can automatically fix some compiler warnings. If you’ve written code in Rust, you’ve probably seen @@ -96,7 +96,7 @@ You can also use the `cargo fix` command to transition your code between different Rust editions. Editions are covered in Appendix E. -## More Lints with Clippy +### More Lints with Clippy The Clippy tool is a collection of lints to analyze your code so you can catch common mistakes and improve your Rust code. @@ -158,7 +158,7 @@ [clippy]: https://github.com/rust-lang/rust-clippy -## IDE Integration Using the Rust Language Server +### IDE Integration Using the Rust Language Server To help IDE integration, the Rust project distributes the *Rust Language Server* (`rls`). This tool speaks the [Language Server
diff --git a/src/doc/book/src/appendix-05-editions.md b/src/doc/book/src/appendix-05-editions.md index ac75c41..db98ecc 100644 --- a/src/doc/book/src/appendix-05-editions.md +++ b/src/doc/book/src/appendix-05-editions.md
@@ -1,4 +1,4 @@ -# Appendix E - Editions +## Appendix E - Editions In Chapter 1, you saw that `cargo new` adds a bit of metadata to your *Cargo.toml* file about an edition. This appendix talks about what that means!
diff --git a/src/doc/book/src/appendix-07-nightly-rust.md b/src/doc/book/src/appendix-07-nightly-rust.md index d8fd0da..bace82f 100644 --- a/src/doc/book/src/appendix-07-nightly-rust.md +++ b/src/doc/book/src/appendix-07-nightly-rust.md
@@ -1,4 +1,4 @@ -# Appendix G - How Rust is Made and “Nightly Rust” +## Appendix G - How Rust is Made and “Nightly Rust” This appendix is about how Rust is made and how that affects you as a Rust developer.
diff --git a/src/doc/book/src/ch00-00-introduction.md b/src/doc/book/src/ch00-00-introduction.md index 7a37ee4..86fe469 100644 --- a/src/doc/book/src/ch00-00-introduction.md +++ b/src/doc/book/src/ch00-00-introduction.md
@@ -104,7 +104,7 @@ chapters, we’ll build small programs together, applying what you’ve learned so far. Chapters 2, 12, and 20 are project chapters; the rest are concept chapters. -Chapter 1 explains how to install Rust, how to write a Hello, world! program, +Chapter 1 explains how to install Rust, how to write a “Hello, world!” program, and how to use Cargo, Rust’s package manager and build tool. Chapter 2 is a hands-on introduction to the Rust language. Here we cover concepts at a high level, and later chapters will provide additional detail. If you want to get
diff --git a/src/doc/book/src/ch01-01-installation.md b/src/doc/book/src/ch01-01-installation.md index 9061389..d7659eb 100644 --- a/src/doc/book/src/ch01-01-installation.md +++ b/src/doc/book/src/ch01-01-installation.md
@@ -126,9 +126,9 @@ ### Local Documentation -The installer also includes a copy of the documentation locally, so you can -read it offline. Run `rustup doc` to open the local documentation in your -browser. +The installation of Rust also includes a copy of the documentation locally, so +you can read it offline. Run `rustup doc` to open the local documentation in +your browser. Any time a type or function is provided by the standard library and you’re not sure what it does or how to use it, use the application programming interface
diff --git a/src/doc/book/src/ch01-02-hello-world.md b/src/doc/book/src/ch01-02-hello-world.md index e82ba49..82a545a 100644 --- a/src/doc/book/src/ch01-02-hello-world.md +++ b/src/doc/book/src/ch01-02-hello-world.md
@@ -20,7 +20,7 @@ your projects there. Open a terminal and enter the following commands to make a *projects* directory -and a directory for the Hello, world! project within the *projects* directory. +and a directory for the “Hello, world!” project within the *projects* directory. For Linux, macOS, and PowerShell on Windows, enter this: @@ -86,7 +86,7 @@ ### Anatomy of a Rust Program -Let’s review in detail what just happened in your Hello, world! program. +Let’s review in detail what just happened in your “Hello, world!” program. Here’s the first piece of the puzzle: ```rust @@ -178,7 +178,7 @@ $ ./main # or .\main.exe on Windows ``` -If *main.rs* was your Hello, world! program, this line would print `Hello, +If *main.rs* was your “Hello, world!” program, this line would print `Hello, world!` to your terminal. If you’re more familiar with a dynamic language, such as Ruby, Python, or
diff --git a/src/doc/book/src/ch01-03-hello-cargo.md b/src/doc/book/src/ch01-03-hello-cargo.md index 34428e5..b40be39 100644 --- a/src/doc/book/src/ch01-03-hello-cargo.md +++ b/src/doc/book/src/ch01-03-hello-cargo.md
@@ -6,9 +6,9 @@ building those libraries. (We call libraries your code needs *dependencies*.) The simplest Rust programs, like the one we’ve written so far, don’t have any -dependencies. So if we had built the Hello, world! project with Cargo, it would -only use the part of Cargo that handles building your code. As you write more -complex Rust programs, you’ll add dependencies, and if you start a project +dependencies. So if we had built the “Hello, world!” project with Cargo, it +would only use the part of Cargo that handles building your code. As you write +more complex Rust programs, you’ll add dependencies, and if you start a project using Cargo, adding dependencies will be much easier to do. Because the vast majority of Rust projects use Cargo, the rest of this book @@ -29,7 +29,7 @@ ### Creating a Project with Cargo Let’s create a new project using Cargo and look at how it differs from our -original Hello, world! project. Navigate back to your *projects* directory (or +original “Hello, world!” project. Navigate back to your *projects* directory (or wherever you decided to store your code). Then, on any operating system, run the following: @@ -99,10 +99,10 @@ } ``` -Cargo has generated a Hello, world! program for you, just like the one we wrote -in Listing 1-1! So far, the differences between our previous project and the -project Cargo generates are that Cargo placed the code in the *src* directory, -and we have a *Cargo.toml* configuration file in the top directory. +Cargo has generated a “Hello, world!” program for you, just like the one we +wrote in Listing 1-1! So far, the differences between our previous project and +the project Cargo generates are that Cargo placed the code in the *src* +directory, and we have a *Cargo.toml* configuration file in the top directory. Cargo expects your source files to live inside the *src* directory. The top-level project directory is just for README files, license information, @@ -110,14 +110,14 @@ helps you organize your projects. There’s a place for everything, and everything is in its place. -If you started a project that doesn’t use Cargo, as we did with the Hello, -world! project, you can convert it to a project that does use Cargo. Move the +If you started a project that doesn’t use Cargo, as we did with the “Hello, +world!” project, you can convert it to a project that does use Cargo. Move the project code into the *src* directory and create an appropriate *Cargo.toml* file. ### Building and Running a Cargo Project -Now let’s look at what’s different when we build and run the Hello, world! +Now let’s look at what’s different when we build and run the “Hello, world!” program with Cargo! From your *hello_cargo* directory, build your project by entering the following command: @@ -237,7 +237,7 @@ * Install the latest stable version of Rust using `rustup` * Update to a newer Rust version * Open locally installed documentation -* Write and run a Hello, world! program using `rustc` directly +* Write and run a “Hello, world!” program using `rustc` directly * Create and run a new project using the conventions of Cargo This is a great time to build a more substantial program to get used to reading
diff --git a/src/doc/book/src/ch02-00-guessing-game-tutorial.md b/src/doc/book/src/ch02-00-guessing-game-tutorial.md index 5651b68..2ecc152 100644 --- a/src/doc/book/src/ch02-00-guessing-game-tutorial.md +++ b/src/doc/book/src/ch02-00-guessing-game-tutorial.md
@@ -201,7 +201,7 @@ .expect("Failed to read line"); ``` -If we hadn’t listed the `use std::io` line at the beginning of the program, we +If we hadn’t put the `use std::io` line at the beginning of the program, we could have written this function call as `std::io::stdin`. The `stdin` function returns an instance of [`std::io::Stdin`][iostdin]<!-- ignore -->, which is a type that represents a handle to the standard input for your terminal. @@ -373,23 +373,28 @@ the bottom beneath the `[dependencies]` section header that Cargo created for you: +<!-- When updating the version of `rand` used, also update the version of +`rand` used in these files so they all match: +* ch07-04-bringing-paths-into-scope-with-the-use-keyword.md +* ch14-03-cargo-workspaces.md +--> + <span class="filename">Filename: Cargo.toml</span> ```toml [dependencies] - -rand = "0.3.14" +rand = "0.5.5" ``` In the *Cargo.toml* file, everything that follows a header is part of a section that continues until another section starts. The `[dependencies]` section is where you tell Cargo which external crates your project depends on and which versions of those crates you require. In this case, we’ll specify the `rand` -crate with the semantic version specifier `0.3.14`. Cargo understands [Semantic +crate with the semantic version specifier `0.5.5`. Cargo understands [Semantic Versioning][semver]<!-- ignore --> (sometimes called *SemVer*), which is a -standard for writing version numbers. The number `0.3.14` is actually shorthand -for `^0.3.14`, which means “any version that has a public API compatible with -version 0.3.14.” +standard for writing version numbers. The number `0.5.5` is actually shorthand +for `^0.5.5`, which means “any version that has a public API compatible with +version 0.5.5.” [semver]: http://semver.org @@ -398,13 +403,19 @@ ```text $ cargo build - Updating registry `https://github.com/rust-lang/crates.io-index` - Downloading rand v0.3.14 - Downloading libc v0.2.14 - Compiling libc v0.2.14 - Compiling rand v0.3.14 + Updating crates.io index + Downloaded rand v0.5.5 + Downloaded libc v0.2.62 + Downloaded rand_core v0.2.2 + Downloaded rand_core v0.3.1 + Downloaded rand_core v0.4.2 + Compiling rand_core v0.4.2 + Compiling libc v0.2.62 + Compiling rand_core v0.3.1 + Compiling rand_core v0.2.2 + Compiling rand v0.5.5 Compiling guessing_game v0.1.0 (file:///projects/guessing_game) - Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs + Finished dev [unoptimized + debuginfo] target(s) in 2.53 s ``` <span class="caption">Listing 2-2: The output from running `cargo build` after @@ -422,8 +433,8 @@ After updating the registry, Cargo checks the `[dependencies]` section and downloads any crates you don’t have yet. In this case, although we only listed -`rand` as a dependency, Cargo also grabbed a copy of `libc`, because `rand` -depends on `libc` to work. After downloading the crates, Rust compiles them and +`rand` as a dependency, Cargo also grabbed `libc` and `rand_core`, because `rand` +depends on those to work. After downloading the crates, Rust compiles them and then compiles the project with the dependencies available. If you immediately run `cargo build` again without making any changes, you @@ -439,7 +450,7 @@ ```text $ cargo build Compiling guessing_game v0.1.0 (file:///projects/guessing_game) - Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs + Finished dev [unoptimized + debuginfo] target(s) in 2.53s ``` These lines show Cargo only updates the build with your tiny change to the @@ -452,7 +463,7 @@ Cargo has a mechanism that ensures you can rebuild the same artifact every time you or anyone else builds your code: Cargo will use only the versions of the dependencies you specified until you indicate otherwise. For example, what -happens if next week version 0.3.15 of the `rand` crate comes out and +happens if next week version 0.5.6 of the `rand` crate comes out and contains an important bug fix but also contains a regression that will break your code? @@ -464,7 +475,7 @@ see that the *Cargo.lock* file exists and use the versions specified there rather than doing all the work of figuring out versions again. This lets you have a reproducible build automatically. In other words, your project will -remain at `0.3.14` until you explicitly upgrade, thanks to the *Cargo.lock* +remain at `0.5.5` until you explicitly upgrade, thanks to the *Cargo.lock* file. #### Updating a Crate to Get a New Version @@ -474,26 +485,25 @@ that fit your specifications in *Cargo.toml*. If that works, Cargo will write those versions to the *Cargo.lock* file. -But by default, Cargo will only look for versions greater than `0.3.0` and less -than `0.4.0`. If the `rand` crate has released two new versions, `0.3.15` and -`0.4.0`, you would see the following if you ran `cargo update`: +But by default, Cargo will only look for versions greater than `0.5.5` and less +than `0.6.0`. If the `rand` crate has released two new versions, `0.5.6` and +`0.6.0`, you would see the following if you ran `cargo update`: ```text $ cargo update - Updating registry `https://github.com/rust-lang/crates.io-index` - Updating rand v0.3.14 -> v0.3.15 + Updating crates.io index + Updating rand v0.5.5 -> v0.5.6 ``` At this point, you would also notice a change in your *Cargo.lock* file noting -that the version of the `rand` crate you are now using is `0.3.15`. +that the version of the `rand` crate you are now using is `0.5.6`. -If you wanted to use `rand` version `0.4.0` or any version in the `0.4.x` +If you wanted to use `rand` version `0.6.0` or any version in the `0.6.x` series, you’d have to update the *Cargo.toml* file to look like this instead: ```toml [dependencies] - -rand = "0.4.0" +rand = "0.6.0" ``` The next time you run `cargo build`, Cargo will update the registry of crates
diff --git a/src/doc/book/src/ch03-01-variables-and-mutability.md b/src/doc/book/src/ch03-01-variables-and-mutability.md index b14bba5..d6a73a0 100644 --- a/src/doc/book/src/ch03-01-variables-and-mutability.md +++ b/src/doc/book/src/ch03-01-variables-and-mutability.md
@@ -65,7 +65,7 @@ you did in Chapter 2, you can make them mutable by adding `mut` in front of the variable name. In addition to allowing this value to change, `mut` conveys intent to future readers of the code by indicating that other parts of the code -will be changing this variable's value. +will be changing this variable’s value. For example, let’s change *src/main.rs* to the following:
diff --git a/src/doc/book/src/ch03-02-data-types.md b/src/doc/book/src/ch03-02-data-types.md index 228e431..482e69f 100644 --- a/src/doc/book/src/ch03-02-data-types.md +++ b/src/doc/book/src/ch03-02-data-types.md
@@ -228,9 +228,9 @@ #### The Tuple Type -A tuple is a general way of grouping together some number of other values -with a variety of types into one compound type. Tuples have a fixed length: -once declared, they cannot grow or shrink in size. +A tuple is a general way of grouping together a number of values with a variety +of types into one compound type. Tuples have a fixed length: once declared, +they cannot grow or shrink in size. We create a tuple by writing a comma-separated list of values inside parentheses. Each position in the tuple has a type, and the types of the @@ -286,8 +286,8 @@ ``` This program creates a tuple, `x`, and then makes new variables for each -element by using their index. As with most programming languages, the first -index in a tuple is 0. +element by using their respective indices. As with most programming languages, +the first index in a tuple is 0. #### The Array Type @@ -318,7 +318,7 @@ An example of when you might want to use an array rather than a vector is in a program that needs to know the names of the months of the year. It’s very unlikely that such a program will need to add or remove months, so you can use -an array because you know it will always contain 12 items: +an array because you know it will always contain 12 elements: ```rust let months = ["January", "February", "March", "April", "May", "June", "July", @@ -334,7 +334,7 @@ ``` Here, `i32` is the type of each element. After the semicolon, the number `5` -indicates the element contains five items. +indicates the array contains five elements. Writing an array’s type this way looks similar to an alternative syntax for initializing an array: if you want to create an array that contains the same
diff --git a/src/doc/book/src/ch06-00-enums.md b/src/doc/book/src/ch06-00-enums.md index 767f866..cf7ea67 100644 --- a/src/doc/book/src/ch06-00-enums.md +++ b/src/doc/book/src/ch06-00-enums.md
@@ -1,7 +1,7 @@ # Enums and Pattern Matching In this chapter we’ll look at *enumerations*, also referred to as *enums*. -Enums allow you to define a type by enumerating its possible values. First, +Enums allow you to define a type by enumerating its possible *variants*. First, we’ll define and use an enum to show how an enum can encode meaning along with data. Next, we’ll explore a particularly useful enum, called `Option`, which expresses that a value can be either something or nothing. Then we’ll look at
diff --git a/src/doc/book/src/ch06-01-defining-an-enum.md b/src/doc/book/src/ch06-01-defining-an-enum.md index 9a56af7..0d25afb 100644 --- a/src/doc/book/src/ch06-01-defining-an-enum.md +++ b/src/doc/book/src/ch06-01-defining-an-enum.md
@@ -5,18 +5,18 @@ with IP addresses. Currently, two major standards are used for IP addresses: version four and version six. These are the only possibilities for an IP address that our program will come across: we can *enumerate* all possible -values, which is where enumeration gets its name. +variants, which is where enumeration gets its name. Any IP address can be either a version four or a version six address, but not both at the same time. That property of IP addresses makes the enum data -structure appropriate, because enum values can only be one of the variants. +structure appropriate, because enum values can only be one of its variants. Both version four and version six addresses are still fundamentally IP addresses, so they should be treated as the same type when the code is handling situations that apply to any kind of IP address. We can express this concept in code by defining an `IpAddrKind` enumeration and -listing the possible kinds an IP address can be, `V4` and `V6`. These are known -as the *variants* of the enum: +listing the possible kinds an IP address can be, `V4` and `V6`. These are the +variants of the enum: ```rust enum IpAddrKind {
diff --git a/src/doc/book/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md b/src/doc/book/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md index ea6e517..05af46c 100644 --- a/src/doc/book/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md +++ b/src/doc/book/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md
@@ -241,6 +241,12 @@ package called `rand` to get random numbers. To use `rand` in our project, we added this line to *Cargo.toml*: +<!-- When updating the version of `rand` used, also update the version of +`rand` used in these files so they all match: +* ch02-00-guessing-game-tutorial.md +* ch14-03-cargo-workspaces.md +--> + <span class="filename">Filename: Cargo.toml</span> ```toml
diff --git a/src/doc/book/src/ch07-05-separating-modules-into-different-files.md b/src/doc/book/src/ch07-05-separating-modules-into-different-files.md index 4a039fa..6b51859 100644 --- a/src/doc/book/src/ch07-05-separating-modules-into-different-files.md +++ b/src/doc/book/src/ch07-05-separating-modules-into-different-files.md
@@ -76,7 +76,7 @@ ## Summary -Rust lets you organize your packages into crates and your crates into modules +Rust lets you split a package into multiple crates and a crate into modules so you can refer to items defined in one module from another module. You can do this by specifying absolute or relative paths. These paths can be brought into scope with a `use` statement so you can use a shorter path for multiple uses of
diff --git a/src/doc/book/src/ch09-02-recoverable-errors-with-result.md b/src/doc/book/src/ch09-02-recoverable-errors-with-result.md index 7de4528..8461e2b 100644 --- a/src/doc/book/src/ch09-02-recoverable-errors-with-result.md +++ b/src/doc/book/src/ch09-02-recoverable-errors-with-result.md
@@ -470,13 +470,13 @@ opportunity to explain all the error handling, so we did it the longer way first. -#### The `?` Operator Can Only Be Used in Functions That Return `Result` +#### The `?` Operator Can Be Used in Functions That Return `Result` -The `?` operator can only be used in functions that have a return type of +The `?` operator can be used in functions that have a return type of `Result`, because it is defined to work in the same way as the `match` expression we defined in Listing 9-6. The part of the `match` that requires a return type of `Result` is `return Err(e)`, so the return type of the function -must be a `Result` to be compatible with this `return`. +can be a `Result` to be compatible with this `return`. Let’s look at what happens if we use the `?` operator in the `main` function, which you’ll recall has a return type of `()`: @@ -505,8 +505,9 @@ ``` This error points out that we’re only allowed to use the `?` operator in a -function that returns `Result<T, E>`. When you’re writing code in a function -that doesn’t return `Result<T, E>`, and you want to use `?` when you call other +function that returns `Result` or `Option` or another type that implements +`std::ops::Try`. When you’re writing code in a function +that doesn’t return one of these types, and you want to use `?` when you call other functions that return `Result<T, E>`, you have two choices to fix this problem. One technique is to change the return type of your function to be `Result<T, E>` if you have no restrictions preventing that. The other technique is to use
diff --git a/src/doc/book/src/ch10-02-traits.md b/src/doc/book/src/ch10-02-traits.md index 8fcf15d..19e873b 100644 --- a/src/doc/book/src/ch10-02-traits.md +++ b/src/doc/book/src/ch10-02-traits.md
@@ -611,12 +611,12 @@ type to have particular behavior. The compiler can then use the trait bound information to check that all the concrete types used with our code provide the correct behavior. In dynamically typed languages, we would get an error at -runtime if we called a method on a type that the type didn’t implement. But -Rust moves these errors to compile time so we’re forced to fix the problems -before our code is even able to run. Additionally, we don’t have to write code -that checks for behavior at runtime because we’ve already checked at compile -time. Doing so improves performance without having to give up the flexibility -of generics. +runtime if we called a method on a type which didn’t implement the type which +defines the method. But Rust moves these errors to compile time so we’re forced +to fix the problems before our code is even able to run. Additionally, we don’t +have to write code that checks for behavior at runtime because we’ve already +checked at compile time. Doing so improves performance without having to give +up the flexibility of generics. Another kind of generic that we’ve already been using is called *lifetimes*. Rather than ensuring that a type has the behavior we want, lifetimes ensure
diff --git a/src/doc/book/src/ch12-04-testing-the-librarys-functionality.md b/src/doc/book/src/ch12-04-testing-the-librarys-functionality.md index 2d1eeb0..e8ed858 100644 --- a/src/doc/book/src/ch12-04-testing-the-librarys-functionality.md +++ b/src/doc/book/src/ch12-04-testing-the-librarys-functionality.md
@@ -196,7 +196,7 @@ </span> The `lines` method returns an iterator. We’ll talk about iterators in depth in -[Chapter 13][ch13]<!-- ignore -->, but recall that you saw this way of using an +[Chapter 13][ch13-iterators]<!-- ignore -->, but recall that you saw this way of using an iterator in [Listing 3-5][ch3-iter]<!-- ignore -->, where we used a `for` loop with an iterator to run some code on each item in a collection. @@ -266,7 +266,7 @@ implementation of the search function while keeping the tests passing to maintain the same functionality. The code in the search function isn’t too bad, but it doesn’t take advantage of some useful features of iterators. We’ll -return to this example in [Chapter 13][ch13]<!-- ignore -->, where we’ll +return to this example in [Chapter 13][ch13-iterators]<!-- ignore -->, where we’ll explore iterators in detail, and look at how to improve it. #### Using the `search` Function in the `run` Function @@ -336,3 +336,4 @@ [ch11-anatomy]: ch11-01-writing-tests.html#the-anatomy-of-a-test-function [ch10-lifetimes]: ch10-03-lifetime-syntax.html [ch3-iter]: ch03-05-control-flow.html#looping-through-a-collection-with-for +[ch13-iterators]: ch13-02-iterators.html
diff --git a/src/doc/book/src/ch13-01-closures.md b/src/doc/book/src/ch13-01-closures.md index f679c14..56f8ed4 100644 --- a/src/doc/book/src/ch13-01-closures.md +++ b/src/doc/book/src/ch13-01-closures.md
@@ -133,11 +133,9 @@ inside the outer `else` doesn’t call it at all, and the code inside the second `else` case calls it once. -<!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 --> - The desired behavior of the `generate_workout` function is to first check -whether the user wants a low-intensity workout (indicated by a number less -than 25) or a high-intensity workout (a number of 25 or greater). +whether the user wants a low-intensity workout (indicated by a number less than +25) or a high-intensity workout (a number of 25 or greater). Low-intensity workout plans will recommend a number of push-ups and sit-ups based on the complex algorithm we’re simulating.
diff --git a/src/doc/book/src/ch14-03-cargo-workspaces.md b/src/doc/book/src/ch14-03-cargo-workspaces.md index 8b8c078..a662ac2 100644 --- a/src/doc/book/src/ch14-03-cargo-workspaces.md +++ b/src/doc/book/src/ch14-03-cargo-workspaces.md
@@ -192,12 +192,17 @@ *add-one/Cargo.toml* file to be able to use the `rand` crate in the `add-one` crate: +<!-- When updating the version of `rand` used, also update the version of +`rand` used in these files so they all match: +* ch02-00-guessing-game-tutorial.md +* ch07-04-bringing-paths-into-scope-with-the-use-keyword.md +--> + <span class="filename">Filename: add-one/Cargo.toml</span> ```toml [dependencies] - -rand = "0.3.14" +rand = "0.5.5" ``` We can now add `use rand;` to the *add-one/src/lib.rs* file, and building the @@ -206,10 +211,10 @@ ```text $ cargo build - Updating registry `https://github.com/rust-lang/crates.io-index` - Downloading rand v0.3.14 + Updating crates.io index + Downloaded rand v0.5.5 --snip-- - Compiling rand v0.3.14 + Compiling rand v0.5.5 Compiling add-one v0.1.0 (file:///projects/add/add-one) Compiling adder v0.1.0 (file:///projects/add/adder) Finished dev [unoptimized + debuginfo] target(s) in 10.18 secs
diff --git a/src/doc/book/src/ch15-03-drop.md b/src/doc/book/src/ch15-03-drop.md index 800de36..333e4e2 100644 --- a/src/doc/book/src/ch15-03-drop.md +++ b/src/doc/book/src/ch15-03-drop.md
@@ -58,7 +58,7 @@ demonstrate when Rust will call `drop`. In `main`, we create two instances of `CustomSmartPointer` and then print -`CustomSmartPointers created.`. At the end of `main`, our instances of +`CustomSmartPointers created`. At the end of `main`, our instances of `CustomSmartPointer` will go out of scope, and Rust will call the code we put in the `drop` method, printing our final message. Note that we didn’t need to call the `drop` method explicitly. @@ -84,7 +84,7 @@ `Drop` trait is that it’s taken care of automatically. Occasionally, however, you might want to clean up a value early. One example is when using smart pointers that manage locks: you might want to force the `drop` method that -releases the lock to run so other code in the same scope can acquire the lock. +releases the lock so that other code in the same scope can acquire the lock. Rust doesn’t let you call the `Drop` trait’s `drop` method manually; instead you have to call the `std::mem::drop` function provided by the standard library if you want to force a value to be dropped before the end of its scope. @@ -146,7 +146,7 @@ # # impl Drop for CustomSmartPointer { # fn drop(&mut self) { -# println!("Dropping CustomSmartPointer!"); +# println!("Dropping CustomSmartPointer with data `{}`!", self.data); # } # } #
diff --git a/src/doc/book/src/ch15-05-interior-mutability.md b/src/doc/book/src/ch15-05-interior-mutability.md index f43d549..34c002b 100644 --- a/src/doc/book/src/ch15-05-interior-mutability.md +++ b/src/doc/book/src/ch15-05-interior-mutability.md
@@ -1,16 +1,14 @@ ## `RefCell<T>` and the Interior Mutability Pattern -<!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 --> - *Interior mutability* is a design pattern in Rust that allows you to mutate data even when there are immutable references to that data; normally, this action is disallowed by the borrowing rules. To mutate data, the pattern uses `unsafe` code inside a data structure to bend Rust’s usual rules that govern -mutation and borrowing. We haven’t yet covered unsafe code; we will in -Chapter 19. We can use types that use the interior mutability pattern when we -can ensure that the borrowing rules will be followed at runtime, even though -the compiler can’t guarantee that. The `unsafe` code involved is then wrapped -in a safe API, and the outer type is still immutable. +mutation and borrowing. We haven’t yet covered unsafe code; we will in Chapter +19. We can use types that use the interior mutability pattern when we can +ensure that the borrowing rules will be followed at runtime, even though the +compiler can’t guarantee that. The `unsafe` code involved is then wrapped in a +safe API, and the outer type is still immutable. Let’s explore this concept by looking at the `RefCell<T>` type that follows the interior mutability pattern.
diff --git a/src/doc/book/src/ch16-02-message-passing.md b/src/doc/book/src/ch16-02-message-passing.md index 56181ea..6b5c23f 100644 --- a/src/doc/book/src/ch16-02-message-passing.md +++ b/src/doc/book/src/ch16-02-message-passing.md
@@ -55,16 +55,14 @@ producer for now, but we’ll add multiple producers when we get this example working. -<!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 --> - The `mpsc::channel` function returns a tuple, the first element of which is the sending end and the second element is the receiving end. The abbreviations `tx` and `rx` are traditionally used in many fields for *transmitter* and *receiver* respectively, so we name our variables as such to indicate each end. We’re using a `let` statement with a pattern that destructures the tuples; we’ll -discuss the use of patterns in `let` statements and destructuring in -Chapter 18. Using a `let` statement this way is a convenient approach to -extract the pieces of the tuple returned by `mpsc::channel`. +discuss the use of patterns in `let` statements and destructuring in Chapter +18. Using a `let` statement this way is a convenient approach to extract the +pieces of the tuple returned by `mpsc::channel`. Let’s move the transmitting end into a spawned thread and have it send one string so the spawned thread is communicating with the main thread, as shown in
diff --git a/src/doc/book/src/ch17-02-trait-objects.md b/src/doc/book/src/ch17-02-trait-objects.md index 6145f21..d7369bf 100644 --- a/src/doc/book/src/ch17-02-trait-objects.md +++ b/src/doc/book/src/ch17-02-trait-objects.md
@@ -275,14 +275,15 @@ means it implements the `draw` method. This concept—of being concerned only with the messages a value responds to -rather than the value’s concrete type—is similar to the concept *duck typing* -in dynamically typed languages: if it walks like a duck and quacks like a duck, -then it must be a duck! In the implementation of `run` on `Screen` in Listing -17-5, `run` doesn’t need to know what the concrete type of each component is. -It doesn’t check whether a component is an instance of a `Button` or a -`SelectBox`, it just calls the `draw` method on the component. By specifying -`Box<dyn Draw>` as the type of the values in the `components` vector, we’ve -defined `Screen` to need values that we can call the `draw` method on. +rather than the value’s concrete type—is similar to the concept of *duck +typing* in dynamically typed languages: if it walks like a duck and quacks +like a duck, then it must be a duck! In the implementation of `run` on `Screen` +in Listing 17-5, `run` doesn’t need to know what the concrete type of each +component is. It doesn’t check whether a component is an instance of a `Button` +or a `SelectBox`, it just calls the `draw` method on the component. By +specifying `Box<dyn Draw>` as the type of the values in the `components` +vector, we’ve defined `Screen` to need values that we can call the `draw` +method on. The advantage of using trait objects and Rust’s type system to write code similar to code using duck typing is that we never have to check whether a
diff --git a/src/doc/book/src/ch18-02-refutability.md b/src/doc/book/src/ch18-02-refutability.md index 55cb038..36fa17d 100644 --- a/src/doc/book/src/ch18-02-refutability.md +++ b/src/doc/book/src/ch18-02-refutability.md
@@ -10,8 +10,9 @@ Function parameters, `let` statements, and `for` loops can only accept irrefutable patterns, because the program cannot do anything meaningful when -values don’t match. The `if let` and `while let` expressions only accept -refutable patterns, because by definition they’re intended to handle possible +values don’t match. The `if let` and `while let` expressions accept +refutable and irrefutable patterns, but the compiler warns against +irrefutable patterns because by definition they’re intended to handle possible failure: the functionality of a conditional is in its ability to perform differently depending on success or failure. @@ -69,9 +70,9 @@ We’ve given the code an out! This code is perfectly valid, although it means we cannot use an irrefutable pattern without receiving an error. If we give `if let` a pattern that will always match, such as `x`, as shown in Listing 18-10, -it will not compile. +the compiler will give a warning. -```rust,ignore,does_not_compile +```rust,ignore if let x = 5 { println!("{}", x); }; @@ -84,11 +85,15 @@ pattern: ```text -error[E0162]: irrefutable if-let pattern - --> <anon>:2:8 +warning: irrefutable if-let pattern + --> <anon>:2:5 | -2 | if let x = 5 { - | ^ irrefutable pattern +2 | / if let x = 5 { +3 | | println!("{}", x); +4 | | }; + | |_^ + | + = note: #[warn(irrefutable_let_patterns)] on by default ``` For this reason, match arms must use refutable patterns, except for the last
diff --git a/src/doc/book/src/ch18-03-pattern-syntax.md b/src/doc/book/src/ch18-03-pattern-syntax.md index 63eab2f..31b96a8 100644 --- a/src/doc/book/src/ch18-03-pattern-syntax.md +++ b/src/doc/book/src/ch18-03-pattern-syntax.md
@@ -711,11 +711,11 @@ match x { Some(50) => println!("Got 50"), - Some(n) if n == y => println!("Matched, n = {:?}", n), + Some(n) if n == y => println!("Matched, n = {}", n), _ => println!("Default case, x = {:?}", x), } - println!("at the end: x = {:?}, y = {:?}", x, y); + println!("at the end: x = {:?}, y = {}", x, y); } ```
diff --git a/src/doc/book/src/ch19-01-unsafe-rust.md b/src/doc/book/src/ch19-01-unsafe-rust.md index 8c32b36..c7956e8 100644 --- a/src/doc/book/src/ch19-01-unsafe-rust.md +++ b/src/doc/book/src/ch19-01-unsafe-rust.md
@@ -251,7 +251,7 @@ This function first gets the total length of the slice. Then it asserts that the index given as a parameter is within the slice by checking whether it’s less than or equal to the length. The assertion means that if we pass an index -that is greater than the index to split the slice at, the function will panic +that is greater than the length to split the slice at, the function will panic before it attempts to use that index. Then we return two mutable slices in a tuple: one from the start of the
diff --git a/src/doc/book/src/ch20-02-multithreaded.md b/src/doc/book/src/ch20-02-multithreaded.md index 5ca9358..1f80bbb 100644 --- a/src/doc/book/src/ch20-02-multithreaded.md +++ b/src/doc/book/src/ch20-02-multithreaded.md
@@ -364,7 +364,7 @@ ``` We still use the `()` after `FnOnce` because this `FnOnce` represents a closure -that takes no parameters and doesn’t return a value. Just like function +that takes no parameters and returns the unit type `()`. Just like function definitions, the return type can be omitted from the signature, but even if we have no parameters, we still need the parentheses.
diff --git a/src/doc/book/convert-quotes.sh b/src/doc/book/tools/convert-quotes.sh similarity index 100% rename from src/doc/book/convert-quotes.sh rename to src/doc/book/tools/convert-quotes.sh
diff --git a/src/doc/book/doc-to-md.sh b/src/doc/book/tools/doc-to-md.sh similarity index 100% rename from src/doc/book/doc-to-md.sh rename to src/doc/book/tools/doc-to-md.sh
diff --git a/src/doc/book/nostarch.sh b/src/doc/book/tools/nostarch.sh similarity index 100% rename from src/doc/book/nostarch.sh rename to src/doc/book/tools/nostarch.sh
diff --git a/src/doc/grammar.md b/src/doc/grammar.md index ee9135b..4501d74 100644 --- a/src/doc/grammar.md +++ b/src/doc/grammar.md
@@ -1,812 +1,7 @@ % Grammar -# Introduction +The Rust grammar may now be found in the [reference]. Additionally, the [grammar +working group] is working on producing a testable grammar. -This document is the primary reference for the Rust programming language grammar. It -provides only one kind of material: - - - Chapters that formally define the language grammar. - -This document does not serve as an introduction to the language. Background -familiarity with the language is assumed. A separate [guide] is available to -help acquire such background. - -This document also does not serve as a reference to the [standard] library -included in the language distribution. Those libraries are documented -separately by extracting documentation attributes from their source code. Many -of the features that one might expect to be language features are library -features in Rust, so what you're looking for may be there, not here. - -[guide]: guide.html -[standard]: std/index.html - -# Notation - -Rust's grammar is defined over Unicode codepoints, each conventionally denoted -`U+XXXX`, for 4 or more hexadecimal digits `X`. _Most_ of Rust's grammar is -confined to the ASCII range of Unicode, and is described in this document by a -dialect of Extended Backus-Naur Form (EBNF), specifically a dialect of EBNF -supported by common automated LL(k) parsing tools such as `llgen`, rather than -the dialect given in ISO 14977. The dialect can be defined self-referentially -as follows: - -```antlr -grammar : rule + ; -rule : nonterminal ':' productionrule ';' ; -productionrule : production [ '|' production ] * ; -production : term * ; -term : element repeats ; -element : LITERAL | IDENTIFIER | '[' productionrule ']' ; -repeats : [ '*' | '+' ] NUMBER ? | NUMBER ? | '?' ; -``` - -Where: - -- Whitespace in the grammar is ignored. -- Square brackets are used to group rules. -- `LITERAL` is a single printable ASCII character, or an escaped hexadecimal - ASCII code of the form `\xQQ`, in single quotes, denoting the corresponding - Unicode codepoint `U+00QQ`. -- `IDENTIFIER` is a nonempty string of ASCII letters and underscores. -- The `repeat` forms apply to the adjacent `element`, and are as follows: - - `?` means zero or one repetition - - `*` means zero or more repetitions - - `+` means one or more repetitions - - NUMBER trailing a repeat symbol gives a maximum repetition count - - NUMBER on its own gives an exact repetition count - -This EBNF dialect should hopefully be familiar to many readers. - -## Unicode productions - -A few productions in Rust's grammar permit Unicode codepoints outside the ASCII -range. We define these productions in terms of character properties specified -in the Unicode standard, rather than in terms of ASCII-range codepoints. The -section [Special Unicode Productions](#special-unicode-productions) lists these -productions. - -## String table productions - -Some rules in the grammar — notably [unary -operators](#unary-operator-expressions), [binary -operators](#binary-operator-expressions), and [keywords](#keywords) — are -given in a simplified form: as a listing of a table of unquoted, printable -whitespace-separated strings. These cases form a subset of the rules regarding -the [token](#tokens) rule, and are assumed to be the result of a -lexical-analysis phase feeding the parser, driven by a DFA, operating over the -disjunction of all such string table entries. - -When such a string enclosed in double-quotes (`"`) occurs inside the grammar, -it is an implicit reference to a single member of such a string table -production. See [tokens](#tokens) for more information. - -# Lexical structure - -## Input format - -Rust input is interpreted as a sequence of Unicode codepoints encoded in UTF-8. -Most Rust grammar rules are defined in terms of printable ASCII-range -codepoints, but a small number are defined in terms of Unicode properties or -explicit codepoint lists. [^inputformat] - -[^inputformat]: Substitute definitions for the special Unicode productions are - provided to the grammar verifier, restricted to ASCII range, when verifying the - grammar in this document. - -## Special Unicode Productions - -The following productions in the Rust grammar are defined in terms of Unicode -properties: `ident`, `non_null`, `non_eol`, `non_single_quote` and -`non_double_quote`. - -### Identifiers - -The `ident` production is any nonempty Unicode string of -the following form: - -- The first character is in one of the following ranges `U+0041` to `U+005A` -("A" to "Z"), `U+0061` to `U+007A` ("a" to "z"), or `U+005F` ("\_"). -- The remaining characters are in the range `U+0030` to `U+0039` ("0" to "9"), -or any of the prior valid initial characters. - -as long as the identifier does _not_ occur in the set of [keywords](#keywords). - -### Delimiter-restricted productions - -Some productions are defined by exclusion of particular Unicode characters: - -- `non_null` is any single Unicode character aside from `U+0000` (null) -- `non_eol` is any single Unicode character aside from `U+000A` (`'\n'`) -- `non_single_quote` is any single Unicode character aside from `U+0027` (`'`) -- `non_double_quote` is any single Unicode character aside from `U+0022` (`"`) - -## Comments - -```antlr -comment : block_comment | line_comment ; -block_comment : "/*" block_comment_body * "*/" ; -block_comment_body : [block_comment | character] * ; -line_comment : "//" non_eol * ; -``` - -**FIXME:** add doc grammar? - -## Whitespace - -```antlr -whitespace_char : '\x20' | '\x09' | '\x0a' | '\x0d' ; -whitespace : [ whitespace_char | comment ] + ; -``` - -## Tokens - -```antlr -simple_token : keyword | unop | binop ; -token : simple_token | ident | literal | symbol | whitespace token ; -``` - -### Keywords - -<p id="keyword-table-marker"></p> - -| | | | | | -|----------|----------|----------|----------|----------| -| _ | abstract | alignof | as | become | -| box | break | const | continue | crate | -| do | else | enum | extern | false | -| final | fn | for | if | impl | -| in | let | loop | macro | match | -| mod | move | mut | offsetof | override | -| priv | proc | pub | pure | ref | -| return | Self | self | sizeof | static | -| struct | super | trait | true | type | -| typeof | unsafe | unsized | use | virtual | -| where | while | yield | | | - - -Each of these keywords has special meaning in its grammar, and all of them are -excluded from the `ident` rule. - -Not all of these keywords are used by the language. Some of them were used -before Rust 1.0, and were left reserved once their implementations were -removed. Some of them were reserved before 1.0 to make space for possible -future features. - -### Literals - -```antlr -lit_suffix : ident; -literal : [ string_lit | char_lit | byte_string_lit | byte_lit | num_lit | bool_lit ] lit_suffix ?; -``` - -The optional `lit_suffix` production is only used for certain numeric literals, -but is reserved for future extension. That is, the above gives the lexical -grammar, but a Rust parser will reject everything but the 12 special cases -mentioned in [Number literals](reference/tokens.html#number-literals) in the -reference. - -#### Character and string literals - -```antlr -char_lit : '\x27' char_body '\x27' ; -string_lit : '"' string_body * '"' | 'r' raw_string ; - -char_body : non_single_quote - | '\x5c' [ '\x27' | common_escape | unicode_escape ] ; - -string_body : non_double_quote - | '\x5c' [ '\x22' | common_escape | unicode_escape ] ; -raw_string : '"' raw_string_body '"' | '#' raw_string '#' ; - -common_escape : '\x5c' - | 'n' | 'r' | 't' | '0' - | 'x' hex_digit 2 -unicode_escape : 'u' '{' hex_digit+ 6 '}'; - -hex_digit : 'a' | 'b' | 'c' | 'd' | 'e' | 'f' - | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' - | dec_digit ; -oct_digit : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' ; -dec_digit : '0' | nonzero_dec ; -nonzero_dec: '1' | '2' | '3' | '4' - | '5' | '6' | '7' | '8' | '9' ; -``` - -#### Byte and byte string literals - -```antlr -byte_lit : "b\x27" byte_body '\x27' ; -byte_string_lit : "b\x22" string_body * '\x22' | "br" raw_byte_string ; - -byte_body : ascii_non_single_quote - | '\x5c' [ '\x27' | common_escape ] ; - -byte_string_body : ascii_non_double_quote - | '\x5c' [ '\x22' | common_escape ] ; -raw_byte_string : '"' raw_byte_string_body '"' | '#' raw_byte_string '#' ; - -``` - -#### Number literals - -```antlr -num_lit : nonzero_dec [ dec_digit | '_' ] * float_suffix ? - | '0' [ [ dec_digit | '_' ] * float_suffix ? - | 'b' [ '1' | '0' | '_' ] + - | 'o' [ oct_digit | '_' ] + - | 'x' [ hex_digit | '_' ] + ] ; - -float_suffix : [ exponent | '.' dec_lit exponent ? ] ? ; - -exponent : ['E' | 'e'] ['-' | '+' ] ? dec_lit ; -dec_lit : [ dec_digit | '_' ] + ; -``` - -#### Boolean literals - -```antlr -bool_lit : [ "true" | "false" ] ; -``` - -The two values of the boolean type are written `true` and `false`. - -### Symbols - -```antlr -symbol : "::" | "->" - | '#' | '[' | ']' | '(' | ')' | '{' | '}' - | ',' | ';' ; -``` - -Symbols are a general class of printable [tokens](#tokens) that play structural -roles in a variety of grammar productions. They are cataloged here for -completeness as the set of remaining miscellaneous printable tokens that do not -otherwise appear as [unary operators](#unary-operator-expressions), [binary -operators](#binary-operator-expressions), or [keywords](#keywords). - -## Paths - -```antlr -expr_path : [ "::" ] ident [ "::" expr_path_tail ] + ; -expr_path_tail : '<' type_expr [ ',' type_expr ] + '>' - | expr_path ; - -type_path : ident [ type_path_tail ] + ; -type_path_tail : '<' type_expr [ ',' type_expr ] + '>' - | "::" type_path ; -``` - -# Syntax extensions - -## Macros - -```antlr -expr_macro_rules : "macro_rules" '!' ident '(' macro_rule * ')' ';' - | "macro_rules" '!' ident '{' macro_rule * '}' ; -macro_rule : '(' matcher * ')' "=>" '(' transcriber * ')' ';' ; -matcher : '(' matcher * ')' | '[' matcher * ']' - | '{' matcher * '}' | '$' ident ':' ident - | '$' '(' matcher * ')' sep_token? [ '*' | '+' ] - | non_special_token ; -transcriber : '(' transcriber * ')' | '[' transcriber * ']' - | '{' transcriber * '}' | '$' ident - | '$' '(' transcriber * ')' sep_token? [ '*' | '+' ] - | non_special_token ; -``` - -# Crates and source files - -**FIXME:** grammar? What production covers #![crate_id = "foo"] ? - -# Items and attributes - -**FIXME:** grammar? - -## Items - -```antlr -item : vis ? mod_item | fn_item | type_item | struct_item | enum_item - | const_item | static_item | trait_item | impl_item | extern_block_item ; -``` - -### Type Parameters - -**FIXME:** grammar? - -### Modules - -```antlr -mod_item : "mod" ident ( ';' | '{' mod '}' ); -mod : [ view_item | item ] * ; -``` - -#### View items - -```antlr -view_item : extern_crate_decl | use_decl ';' ; -``` - -##### Extern crate declarations - -```antlr -extern_crate_decl : "extern" "crate" crate_name -crate_name: ident | ( ident "as" ident ) -``` - -##### Use declarations - -```antlr -use_decl : vis ? "use" [ path "as" ident - | path_glob ] ; - -path_glob : ident [ "::" [ path_glob - | '*' ] ] ? - | '{' path_item [ ',' path_item ] * '}' ; - -path_item : ident | "self" ; -``` - -### Functions - -**FIXME:** grammar? - -#### Generic functions - -**FIXME:** grammar? - -#### Unsafety - -**FIXME:** grammar? - -##### Unsafe functions - -**FIXME:** grammar? - -##### Unsafe blocks - -**FIXME:** grammar? - -#### Diverging functions - -**FIXME:** grammar? - -### Type definitions - -**FIXME:** grammar? - -### Structures - -**FIXME:** grammar? - -### Enumerations - -**FIXME:** grammar? - -### Constant items - -```antlr -const_item : "const" ident ':' type '=' expr ';' ; -``` - -### Static items - -```antlr -static_item : "static" ident ':' type '=' expr ';' ; -``` - -#### Mutable statics - -**FIXME:** grammar? - -### Traits - -**FIXME:** grammar? - -### Implementations - -**FIXME:** grammar? - -### External blocks - -```antlr -extern_block_item : "extern" '{' extern_block '}' ; -extern_block : [ foreign_fn ] * ; -``` - -## Visibility and Privacy - -```antlr -vis : "pub" ; -``` -### Re-exporting and Visibility - -See [Use declarations](#use-declarations). - -## Attributes - -```antlr -attribute : '#' '!' ? '[' meta_item ']' ; -meta_item : ident [ '=' literal - | '(' meta_seq ')' ] ? ; -meta_seq : meta_item [ ',' meta_seq ] ? ; -``` - -# Statements and expressions - -## Statements - -```antlr -stmt : decl_stmt | expr_stmt | ';' ; -``` - -### Declaration statements - -```antlr -decl_stmt : item | let_decl ; -``` - -#### Item declarations - -See [Items](#items). - -#### Variable declarations - -```antlr -let_decl : "let" pat [':' type ] ? [ init ] ? ';' ; -init : [ '=' ] expr ; -``` - -### Expression statements - -```antlr -expr_stmt : expr ';' ; -``` - -## Expressions - -```antlr -expr : literal | path | tuple_expr | unit_expr | struct_expr - | block_expr | method_call_expr | field_expr | array_expr - | idx_expr | range_expr | unop_expr | binop_expr - | paren_expr | call_expr | lambda_expr | while_expr - | loop_expr | break_expr | continue_expr | for_expr - | if_expr | match_expr | if_let_expr | while_let_expr - | return_expr ; -``` - -#### Lvalues, rvalues and temporaries - -**FIXME:** grammar? - -#### Moved and copied types - -**FIXME:** Do we want to capture this in the grammar as different productions? - -### Literal expressions - -See [Literals](#literals). - -### Path expressions - -See [Paths](#paths). - -### Tuple expressions - -```antlr -tuple_expr : '(' [ expr [ ',' expr ] * | expr ',' ] ? ')' ; -``` - -### Unit expressions - -```antlr -unit_expr : "()" ; -``` - -### Structure expressions - -```antlr -struct_expr_field_init : ident | ident ':' expr ; -struct_expr : expr_path '{' struct_expr_field_init - [ ',' struct_expr_field_init ] * - [ ".." expr ] '}' | - expr_path '(' expr - [ ',' expr ] * ')' | - expr_path ; -``` - -### Block expressions - -```antlr -block_expr : '{' [ stmt | item ] * - [ expr ] '}' ; -``` - -### Method-call expressions - -```antlr -method_call_expr : expr '.' ident paren_expr_list ; -``` - -### Field expressions - -```antlr -field_expr : expr '.' ident ; -``` - -### Array expressions - -```antlr -array_expr : '[' "mut" ? array_elems? ']' ; - -array_elems : [expr [',' expr]*] | [expr ';' expr] ; -``` - -### Index expressions - -```antlr -idx_expr : expr '[' expr ']' ; -``` - -### Range expressions - -```antlr -range_expr : expr ".." expr | - expr ".." | - ".." expr | - ".." ; -``` - -### Unary operator expressions - -```antlr -unop_expr : unop expr ; -unop : '-' | '*' | '!' ; -``` - -### Binary operator expressions - -```antlr -binop_expr : expr binop expr | type_cast_expr - | assignment_expr | compound_assignment_expr ; -binop : arith_op | bitwise_op | lazy_bool_op | comp_op -``` - -#### Arithmetic operators - -```antlr -arith_op : '+' | '-' | '*' | '/' | '%' ; -``` - -#### Bitwise operators - -```antlr -bitwise_op : '&' | '|' | '^' | "<<" | ">>" ; -``` - -#### Lazy boolean operators - -```antlr -lazy_bool_op : "&&" | "||" ; -``` - -#### Comparison operators - -```antlr -comp_op : "==" | "!=" | '<' | '>' | "<=" | ">=" ; -``` - -#### Type cast expressions - -```antlr -type_cast_expr : value "as" type ; -``` - -#### Assignment expressions - -```antlr -assignment_expr : expr '=' expr ; -``` - -#### Compound assignment expressions - -```antlr -compound_assignment_expr : expr [ arith_op | bitwise_op ] '=' expr ; -``` - -### Grouped expressions - -```antlr -paren_expr : '(' expr ')' ; -``` - -### Call expressions - -```antlr -expr_list : [ expr [ ',' expr ]* ] ? ; -paren_expr_list : '(' expr_list ')' ; -call_expr : expr paren_expr_list ; -``` - -### Lambda expressions - -```antlr -ident_list : [ ident [ ',' ident ]* ] ? ; -lambda_expr : '|' ident_list '|' expr ; -``` - -### While loops - -```antlr -while_expr : [ lifetime ':' ] ? "while" no_struct_literal_expr '{' block '}' ; -``` - -### Infinite loops - -```antlr -loop_expr : [ lifetime ':' ] ? "loop" '{' block '}'; -``` - -### Break expressions - -```antlr -break_expr : "break" [ lifetime ] ?; -``` - -### Continue expressions - -```antlr -continue_expr : "continue" [ lifetime ] ?; -``` - -### For expressions - -```antlr -for_expr : [ lifetime ':' ] ? "for" pat "in" no_struct_literal_expr '{' block '}' ; -``` - -### If expressions - -```antlr -if_expr : "if" no_struct_literal_expr '{' block '}' - else_tail ? ; - -else_tail : "else" [ if_expr | if_let_expr - | '{' block '}' ] ; -``` - -### Match expressions - -```antlr -match_expr : "match" no_struct_literal_expr '{' match_arm * '}' ; - -match_arm : attribute * match_pat "=>" [ expr "," | '{' block '}' ] ; - -match_pat : pat [ '|' pat ] * [ "if" expr ] ? ; -``` - -### If let expressions - -```antlr -if_let_expr : "if" "let" pat '=' expr '{' block '}' - else_tail ? ; -``` - -### While let loops - -```antlr -while_let_expr : [ lifetime ':' ] ? "while" "let" pat '=' expr '{' block '}' ; -``` - -### Return expressions - -```antlr -return_expr : "return" expr ? ; -``` - -# Type system - -**FIXME:** is this entire chapter relevant here? Or should it all have been covered by some production already? - -## Types - -### Primitive types - -**FIXME:** grammar? - -#### Machine types - -**FIXME:** grammar? - -#### Machine-dependent integer types - -**FIXME:** grammar? - -### Textual types - -**FIXME:** grammar? - -### Tuple types - -**FIXME:** grammar? - -### Array, and Slice types - -**FIXME:** grammar? - -### Structure types - -**FIXME:** grammar? - -### Enumerated types - -**FIXME:** grammar? - -### Pointer types - -**FIXME:** grammar? - -### Function types - -**FIXME:** grammar? - -### Closure types - -```antlr -closure_type := [ 'unsafe' ] [ '<' lifetime-list '>' ] '|' arg-list '|' - [ ':' bound-list ] [ '->' type ] -lifetime-list := lifetime | lifetime ',' lifetime-list -arg-list := ident ':' type | ident ':' type ',' arg-list -``` - -### Never type -An empty type - -```antlr -never_type : "!" ; -``` - -### Object types - -**FIXME:** grammar? - -### Type parameters - -**FIXME:** grammar? - -### Type parameter bounds - -```antlr -bound-list := bound | bound '+' bound-list '+' ? -bound := ty_bound | lt_bound -lt_bound := lifetime -ty_bound := ty_bound_noparen | (ty_bound_noparen) -ty_bound_noparen := [?] [ for<lt_param_defs> ] simple_path -``` - -### Self types - -**FIXME:** grammar? - -## Type kinds - -**FIXME:** this is probably not relevant to the grammar... - -# Memory and concurrency models - -**FIXME:** is this entire chapter relevant here? Or should it all have been covered by some production already? - -## Memory model - -### Memory allocation and lifetime - -### Memory ownership - -### Variables - -### Boxes - -## Threads - -### Communication between threads - -### Thread lifecycle +[reference]: https://doc.rust-lang.org/reference/ +[grammar working group]: https://github.com/rust-lang/wg-grammar
diff --git a/src/doc/nomicon/src/atomics.md b/src/doc/nomicon/src/atomics.md index 4cd209a..f750f09 100644 --- a/src/doc/nomicon/src/atomics.md +++ b/src/doc/nomicon/src/atomics.md
@@ -1,20 +1,23 @@ # Atomics -Rust pretty blatantly just inherits C11's memory model for atomics. This is not +Rust pretty blatantly just inherits the memory model for atomics from C++20. This is not due to this model being particularly excellent or easy to understand. Indeed, this model is quite complex and known to have [several flaws][C11-busted]. Rather, it is a pragmatic concession to the fact that *everyone* is pretty bad at modeling atomics. At very least, we can benefit from existing tooling and -research around C. +research around the C/C++ memory model. +(You'll often see this model referred to as "C/C++11" or just "C11". C just copies +the C++ memory model; and C++11 was the first version of the model but it has +received some bugfixes since then.) Trying to fully explain the model in this book is fairly hopeless. It's defined in terms of madness-inducing causality graphs that require a full book to properly understand in a practical way. If you want all the nitty-gritty -details, you should check out [C's specification (Section 7.17)][C11-model]. +details, you should check out the [C++20 draft specification (Section 31)][C++-model]. Still, we'll try to cover the basics and some of the problems Rust developers face. -The C11 memory model is fundamentally about trying to bridge the gap between the +The C++ memory model is fundamentally about trying to bridge the gap between the semantics we want, the optimizations compilers want, and the inconsistent chaos our hardware wants. *We* would like to just write programs and have them do exactly what we said but, you know, fast. Wouldn't that be great? @@ -113,7 +116,7 @@ # Data Accesses -The C11 memory model attempts to bridge the gap by allowing us to talk about the +The C++ memory model attempts to bridge the gap by allowing us to talk about the *causality* of our program. Generally, this is by establishing a *happens before* relationship between parts of the program and the threads that are running them. This gives the hardware and compiler room to optimize the program @@ -148,7 +151,7 @@ * Acquire * Relaxed -(Note: We explicitly do not expose the C11 *consume* ordering) +(Note: We explicitly do not expose the C++ *consume* ordering) TODO: negative reasoning vs positive reasoning? TODO: "can't forget to synchronize" @@ -252,4 +255,4 @@ [C11-busted]: http://plv.mpi-sws.org/c11comp/popl15.pdf -[C11-model]: http://www.open-std.org/jtc1/sc22/wg14/www/standards.html#9899 +[C++-model]: http://eel.is/c++draft/atomics.order
diff --git a/src/doc/nomicon/src/working-with-unsafe.md b/src/doc/nomicon/src/working-with-unsafe.md index 1864dd1..c29a75a 100644 --- a/src/doc/nomicon/src/working-with-unsafe.md +++ b/src/doc/nomicon/src/working-with-unsafe.md
@@ -16,10 +16,14 @@ } ``` -This function is safe and correct. We check that the index is in bounds, and if it -is, index into the array in an unchecked manner. But even in such a trivial -function, the scope of the unsafe block is questionable. Consider changing the -`<` to a `<=`: +This function is safe and correct. We check that the index is in bounds, and if +it is, index into the array in an unchecked manner. We say that such a correct +unsafely implemented function is *sound*, meaning that safe code cannot cause +Undefined Behavior through it (which, remember, is the single fundamental +property of Safe Rust). + +But even in such a trivial function, the scope of the unsafe block is +questionable. Consider changing the `<` to a `<=`: ```rust fn index(idx: usize, arr: &[u8]) -> Option<u8> { @@ -33,10 +37,10 @@ } ``` -This program is now unsound, and yet *we only modified safe code*. This is the -fundamental problem of safety: it's non-local. The soundness of our unsafe -operations necessarily depends on the state established by otherwise -"safe" operations. +This program is now *unsound*, Safe Rust can cause Undefined Behavior, and yet +*we only modified safe code*. This is the fundamental problem of safety: it's +non-local. The soundness of our unsafe operations necessarily depends on the +state established by otherwise "safe" operations. Safety is modular in the sense that opting into unsafety doesn't require you to consider arbitrary other kinds of badness. For instance, doing an unchecked
diff --git a/src/doc/reference/.travis.yml b/src/doc/reference/.travis.yml index 3d8979f..7330450 100644 --- a/src/doc/reference/.travis.yml +++ b/src/doc/reference/.travis.yml
@@ -1,7 +1,8 @@ -language: rust +language: shell -rust: - - nightly +before_install: + - curl -sSL https://sh.rustup.rs | sh -s -- -y --default-toolchain=nightly --profile=minimal -c rust-docs + - export PATH="$HOME/.cargo/bin:$PATH" install: - travis_retry curl -Lf https://github.com/rust-lang-nursery/mdBook/releases/download/v0.3.1/mdbook-v0.3.1-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=$HOME/.cargo/bin
diff --git a/src/doc/reference/src/SUMMARY.md b/src/doc/reference/src/SUMMARY.md index b1ba241..fb0e657 100644 --- a/src/doc/reference/src/SUMMARY.md +++ b/src/doc/reference/src/SUMMARY.md
@@ -45,6 +45,7 @@ - [Diagnostics](attributes/diagnostics.md) - [Code generation](attributes/codegen.md) - [Limits](attributes/limits.md) + - [Type System](attributes/type_system.md) - [Statements and expressions](statements-and-expressions.md) - [Statements](statements.md)
diff --git a/src/doc/reference/src/attributes.md b/src/doc/reference/src/attributes.md index 059d4de..dfffe5c 100644 --- a/src/doc/reference/src/attributes.md +++ b/src/doc/reference/src/attributes.md
@@ -50,6 +50,9 @@ * [Generic lifetime or type parameter][generics] accept outer attributes. * Expressions accept outer attributes in limited situations, see [Expression Attributes] for details. +* [Function][functions], [closure]] and [function pointer] + parameters accept outer attributes. This includes attributes on variadic parameters + denoted with `...` in function pointers and [external blocks][variadic functions]. Some examples of attributes: @@ -86,8 +89,7 @@ ## Meta Item Attribute Syntax A "meta item" is the syntax used for the _Attr_ rule by most [built-in -attributes] and the [`meta` macro fragment specifier]. It has the following -grammar: +attributes]. It has the following grammar: > **<sup>Syntax</sup>**\ > _MetaItem_ :\ @@ -236,6 +238,9 @@ - Features - `feature` — Used to enable unstable or experimental compiler features. See [The Unstable Book] for features implemented in `rustc`. +- Type System + - [`non_exhaustive`] — Indicate that a type will have more fields/variants + added in future. [Doc comments]: comments.md#doc-comments [ECMA-334]: https://www.ecma-international.org/publications/standards/Ecma-334.htm @@ -268,7 +273,6 @@ [`link`]: items/external-blocks.md#the-link-attribute [`macro_export`]: macros-by-example.md#path-based-scope [`macro_use`]: macros-by-example.md#the-macro_use-attribute -[`meta` macro fragment specifier]: macros-by-example.md [`must_use`]: attributes/diagnostics.md#the-must_use-attribute [`no_builtins`]: attributes/codegen.md#the-no_builtins-attribute [`no_implicit_prelude`]: items/modules.md#prelude-items @@ -276,6 +280,7 @@ [`no_main`]: crates-and-source-files.md#the-no_main-attribute [`no_mangle`]: abi.md#the-no_mangle-attribute [`no_std`]: crates-and-source-files.md#preludes-and-no_std +[`non_exhaustive`]: attributes/type_system.md#the-non_exhaustive-attribute [`panic_handler`]: runtime.md#the-panic_handler-attribute [`path`]: items/modules.md#the-path-attribute [`proc_macro_attribute`]: procedural-macros.md#attribute-macros @@ -306,3 +311,6 @@ [statements]: statements.md [struct]: items/structs.md [union]: items/unions.md +[closure]: expressions/closure-expr.md +[function pointer]: types/function-pointer.md +[variadic functions]: items/external-blocks.html#variadic-functions
diff --git a/src/doc/reference/src/attributes/type_system.md b/src/doc/reference/src/attributes/type_system.md new file mode 100644 index 0000000..52bbb68 --- /dev/null +++ b/src/doc/reference/src/attributes/type_system.md
@@ -0,0 +1,140 @@ +# Type system attributes + +The following [attributes] are used for changing how a type can be used. + +## The `non_exhaustive` attribute + +The *`non_exhaustive` attribute* indicates that a type or variant may have +more fields or variants added in the future. It can be applied to +[`struct`s][struct], [`enum`s][enum], and `enum` variants. + +The `non_exhaustive` attribute uses the [_MetaWord_] syntax and thus does not +take any inputs. + +Within the defining crate, `non_exhaustive` has no effect. + +```rust +#[non_exhaustive] +pub struct Config { + pub window_width: u16, + pub window_height: u16, +} + +#[non_exhaustive] +pub enum Error { + Message(String), + Other, +} + +pub enum Message { + #[non_exhaustive] Send { from: u32, to: u32, contents: String }, + #[non_exhaustive] Reaction(u32), + #[non_exhaustive] Quit, +} + +// Non-exhaustive structs can be constructed as normal within the defining crate. +let config = Config { window_width: 640, window_height: 480 }; + +// Non-exhaustive structs can be matched on exhaustively within the defining crate. +if let Config { window_width, window_height } = config { + // ... +} + +let error = Error::Other; +let message = Message::Reaction(3); + +// Non-exhaustive enums can be matched on exhaustively within the defining crate. +match error { + Error::Message(ref s) => { }, + Error::Other => { }, +} + +match message { + // Non-exhaustive variants can be matched on exhaustively within the defining crate. + Message::Send { from, to, contents } => { }, + Message::Reaction(id) => { }, + Message::Quit => { }, +} +``` + +Outside of the defining crate, types annotated with `non_exhaustive` have limitations that +preserve backwards compatibility when new fields or variants are added. + +Non-exhaustive types cannot be constructed outside of the defining crate: + +- Non-exhaustive variants ([`struct`][struct] or [`enum` variant][enum]) cannot be constructed + with a [_StructExpression_] \(including with [functional update syntax]). +- [`enum`][enum] instances can be constructed in an [_EnumerationVariantExpression_]. + +```rust,ignore (requires multiple crates) +// `Config`, `Error`, and `Message` are types defined in an upstream crate that have been +// annotated as `#[non_exhaustive]`. +use upstream::{Config, Error, Message}; + +// Cannot construct an instance of `Config`, if new fields were added in +// a new version of `upstream` then this would fail to compile, so it is +// disallowed. +let config = Config { window_width: 640, window_height: 480 }; + +// Can construct an instance of `Error`, new variants being introduced would +// not result in this failing to compile. +let error = Error::Message("foo".to_string()); + +// Cannot construct an instance of `Message::Send` or `Message::Reaction`, +// if new fields were added in a new version of `upstream` then this would +// fail to compile, so it is disallowed. +let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), }; +let message = Message::Reaction(0); + +// Cannot construct an instance of `Message::Quit`, if this were converted to +// a tuple-variant `upstream` then this would fail to compile. +let message = Message::Quit; +``` + +There are limitations when matching on non-exhaustive types outside of the defining crate: + +- When pattern matching on a non-exhaustive variant ([`struct`][struct] or [`enum` variant][enum]), + a [_StructPattern_] must be used which must include a `..`. Tuple variant constructor visibility + is lowered to `min($vis, pub(crate))`. +- When pattern matching on a non-exhaustive [`enum`][enum], matching on a variant does not + contribute towards the exhaustiveness of the arms. + +```rust, ignore (requires multiple crates) +// `Config`, `Error`, and `Message` are types defined in an upstream crate that have been +// annotated as `#[non_exhaustive]`. +use upstream::{Config, Error, Message}; + +// Cannot match on a non-exhaustive enum without including a wildcard arm. +match error { + Error::Message(ref s) => { }, + Error::Other => { }, + // would compile with: `_ => {},` +} + +// Cannot match on a non-exhaustive struct without a wildcard. +if let Ok(Config { window_width, window_height }) = config { + // would compile with: `..` +} + +match message { + // Cannot match on a non-exhaustive struct enum variant without including a wildcard. + Message::Send { from, to, contents } => { }, + // Cannot match on a non-exhaustive tuple or unit enum variant. + Message::Reaction(type) => { }, + Message::Quit => { }, +} +``` + +Non-exhaustive types are always considered inhabited in downstream crates. + +[_EnumerationVariantExpression_]: ../expressions/enum-variant-expr.md +[_MetaWord_]: ../attributes.md#meta-item-attribute-syntax +[_StructExpression_]: ../expressions/struct-expr.md +[_StructPattern_]: ../patterns.md#struct-patterns +[_TupleStructPattern_]: ../patterns.md#tuple-struct-patterns +[`if let`]: ../expressions/if-expr.md#if-let-expressions +[`match`]: ../expressions/match-expr.md +[attributes]: ../attributes.md +[enum]: ../items/enumerations.md +[functional update syntax]: ../expressions/struct-expr.md#functional-update-syntax +[struct]: ../items/structs.md
diff --git a/src/doc/reference/src/expressions/await-expr.md b/src/doc/reference/src/expressions/await-expr.md index 95037d7..a6e7dc5 100644 --- a/src/doc/reference/src/expressions/await-expr.md +++ b/src/doc/reference/src/expressions/await-expr.md
@@ -52,12 +52,13 @@ equivalent to the following (this desugaring is not normative): ```rust,ignore -let future = /* <expr> */; -loop { - let mut pin = unsafe { Pin::new_unchecked(&mut future) }; - match Pin::future::poll(Pin::borrow(&mut pin), &mut current_context) { - Poll::Ready(r) => break r, - Poll::Pending => yield Poll::Pending, +match /* <expr> */ { + mut pinned => loop { + let mut pin = unsafe { Pin::new_unchecked(&mut pinned) }; + match Pin::future::poll(Pin::borrow(&mut pin), &mut current_context) { + Poll::Ready(r) => break r, + Poll::Pending => yield Poll::Pending, + } } } ```
diff --git a/src/doc/reference/src/expressions/closure-expr.md b/src/doc/reference/src/expressions/closure-expr.md index aa9299b..74b8e20 100644 --- a/src/doc/reference/src/expressions/closure-expr.md +++ b/src/doc/reference/src/expressions/closure-expr.md
@@ -10,7 +10,7 @@ > _ClosureParam_ (`,` _ClosureParam_)<sup>\*</sup> `,`<sup>?</sup> > > _ClosureParam_ :\ -> [_Pattern_] ( `:` [_Type_] )<sup>?</sup> +> [_OuterAttribute_]<sup>\*</sup> [_Pattern_] ( `:` [_Type_] )<sup>?</sup> A _closure expression_ defines a closure and denotes it as a value, in a single expression. A closure expression is a pipe-symbol-delimited (`|`) list of @@ -67,9 +67,15 @@ ten_times(move |j| println!("{}, {}", word, j)); ``` +## Attributes on closure parameters + +Attributes on closure parameters follow the same rules and restrictions as +[regular function parameters]. + [block]: block-expr.md [function definitions]: ../items/functions.md [patterns]: ../patterns.md +[regular function parameters]: ../items/functions.md#attributes-on-function-parameters [_Expression_]: ../expressions.md [_BlockExpression_]: block-expr.md @@ -77,3 +83,4 @@ [_Pattern_]: ../patterns.md [_Type_]: ../types.md#type-expressions [`let` binding]: ../statements.md#let-statements +[_OuterAttribute_]: ../attributes.md \ No newline at end of file
diff --git a/src/doc/reference/src/glossary.md b/src/doc/reference/src/glossary.md index 6dc22b8..36c0c93 100644 --- a/src/doc/reference/src/glossary.md +++ b/src/doc/reference/src/glossary.md
@@ -65,6 +65,12 @@ An [item] that is not a member of an [implementation], such as a *free function* or a *free const*. Contrast to an [associated item]. +### Inhabited + +A type is inhabited if it has constructors and therefore can be instantiated. An inhabited type is +not "empty" in the sense that there can be values of the type. Opposite of +[Uninhabited](#uninhabited). + ### Inherent implementation An [implementation] that applies to a nominal type, not to a trait-type pair. @@ -159,6 +165,13 @@ or unintended computation; or platform-specific results. [More][undefined-behavior]. +### Uninhabited + +A type is uninhabited if it has no constructors and therefore can never be instantiated. An +uninhabited type is "empty" in the sense that there are no values of the type. The canonical +example of an uninhabited type is the [never type] `!`, or an enum with no variants +`enum Never { }`. Opposite of [Inhabited](#inhabited). + [alignment]: type-layout.md#size-and-alignment [associated item]: #associated-item [enums]: items/enumerations.md @@ -168,6 +181,7 @@ [inherent implementation]: items/implementations.md#inherent-implementations [item]: items.md [method]: items/associated-items.md#methods +[never type]: types/never.md [object safety]: items/traits.md#object-safety [structs]: items/structs.md [trait objects]: types/trait-object.md
diff --git a/src/doc/reference/src/items/associated-items.md b/src/doc/reference/src/items/associated-items.md index 29ea6da..a7bc3de 100644 --- a/src/doc/reference/src/items/associated-items.md +++ b/src/doc/reference/src/items/associated-items.md
@@ -86,8 +86,13 @@ > [_BlockExpression_] > > _SelfParam_ :\ -> (`&` | `&` [_Lifetime_])<sup>?</sup> `mut`<sup>?</sup> `self`\ -> | `mut`<sup>?</sup> `self` (`:` [_Type_])<sup>?</sup> +> [_OuterAttribute_]<sup>\*</sup> ( _ShorthandSelf_ | _TypedSelf_ ) +> +> _ShorthandSelf_ :\ +> (`&` | `&` [_Lifetime_])<sup>?</sup> `mut`<sup>?</sup> `self` +> +> _TypedSelf_ :\ +> `mut`<sup>?</sup> `self` `:` [_Type_] Associated functions whose first parameter is named `self` are called *methods* and may be invoked using the [method call operator], for example, `x.foo()`, as @@ -190,6 +195,11 @@ > methods with anonymous parameters (e.g. `fn foo(u8)`). This is deprecated and > an error as of the 2018 edition. All parameters must have an argument name. +#### Attributes on method parameters + +Attributes on method parameters follow the same rules and restrictions as +[regular function parameters]. + ## Associated Types *Associated types* are [type aliases] associated with another type. Associated @@ -336,6 +346,7 @@ [`Box<Self>`]: ../special-types-and-traits.md#boxt [`Pin<P>`]: ../special-types-and-traits.md#pinp [`Rc<Self>`]: ../special-types-and-traits.md#rct +[_OuterAttribute_]: ../attributes.md [traits]: traits.md [type aliases]: type-aliases.md [inherent implementations]: implementations.md#inherent-implementations @@ -349,3 +360,4 @@ [function item]: ../types/function-item.md [method call operator]: ../expressions/method-call-expr.md [path]: ../paths.md +[regular function parameters]: functions.md#attributes-on-function-parameters \ No newline at end of file
diff --git a/src/doc/reference/src/items/external-blocks.md b/src/doc/reference/src/items/external-blocks.md index f3e692a..6537084 100644 --- a/src/doc/reference/src/items/external-blocks.md +++ b/src/doc/reference/src/items/external-blocks.md
@@ -8,9 +8,10 @@ > `}` > > _ExternalItem_ :\ -> [_OuterAttribute_]<sup>\*</sup>\ -> [_Visibility_]<sup>?</sup>\ -> ( _ExternalStaticItem_ | _ExternalFunctionItem_ ) +> [_OuterAttribute_]<sup>\*</sup> (\ +> [_MacroInvocationSemi_]\ +> | ( [_Visibility_]<sup>?</sup> ( _ExternalStaticItem_ | _ExternalFunctionItem_ ) )\ +> ) > > _ExternalStaticItem_ :\ > `static` `mut`<sup>?</sup> [IDENTIFIER] `:` [_Type_] `;` @@ -24,14 +25,14 @@ > _NamedFunctionParam_ ( `,` _NamedFunctionParam_ )<sup>\*</sup> `,`<sup>?</sup> > > _NamedFunctionParam_ :\ -> ( [IDENTIFIER] | `_` ) `:` [_Type_] +> [_OuterAttribute_]<sup>\*</sup> ( [IDENTIFIER] | `_` ) `:` [_Type_] > > _NamedFunctionParametersWithVariadics_ :\ -> ( _NamedFunctionParam_ `,` )<sup>\*</sup> _NamedFunctionParam_ `,` `...` +> ( _NamedFunctionParam_ `,` )<sup>\*</sup> _NamedFunctionParam_ `,` [_OuterAttribute_]<sup>\*</sup> `...` External blocks provide _declarations_ of items that are not _defined_ in the current crate and are the basis of Rust's foreign function interface. These are -akin to unchecked imports. +akin to unchecked imports. Two kind of item _declarations_ are allowed in external blocks: [functions] and [statics]. Calling functions or accessing statics that are declared in external @@ -162,6 +163,11 @@ } ``` +### Attributes on function parameters + +Attributes on extern function parameters follow the same rules and +restrictions as [regular function parameters]. + [IDENTIFIER]: ../identifiers.md [WebAssembly module]: https://webassembly.github.io/spec/core/syntax/modules.html [functions]: functions.md @@ -170,6 +176,7 @@ [_FunctionReturnType_]: functions.md [_Generics_]: generics.md [_InnerAttribute_]: ../attributes.md +[_MacroInvocationSemi_]: ../macros.md#macro-invocation [_MetaListNameValueStr_]: ../attributes.md#meta-item-attribute-syntax [_MetaNameValueStr_]: ../attributes.md#meta-item-attribute-syntax [_OuterAttribute_]: ../attributes.md @@ -177,3 +184,4 @@ [_Visibility_]: ../visibility-and-privacy.md [_WhereClause_]: generics.md#where-clauses [attributes]: ../attributes.md +[regular function parameters]: functions.md#attributes-on-function-parameters \ No newline at end of file
diff --git a/src/doc/reference/src/items/functions.md b/src/doc/reference/src/items/functions.md index 63f436f..4bc4851 100644 --- a/src/doc/reference/src/items/functions.md +++ b/src/doc/reference/src/items/functions.md
@@ -20,7 +20,7 @@ > _FunctionParam_ (`,` _FunctionParam_)<sup>\*</sup> `,`<sup>?</sup> > > _FunctionParam_ :\ -> [_Pattern_] `:` [_Type_] +> [_OuterAttribute_]<sup>\*</sup> [_Pattern_] `:` [_Type_] > > _FunctionReturnType_ :\ > `->` [_Type_] @@ -180,12 +180,13 @@ ## Const functions -Functions qualified with the `const` keyword are const functions. _Const -functions_ can be called from within [const context]s. When called from a const -context, the function is interpreted by the compiler at compile time. The -interpretation happens in the environment of the compilation target and not the -host. So `usize` is `32` bits if you are compiling against a `32` bit system, -irrelevant of whether you are building on a `64` bit or a `32` bit system. +Functions qualified with the `const` keyword are const functions, as are +[tuple struct] and [tuple variant] constructors. _Const functions_ can be +called from within [const context]s. When called from a const context, the +function is interpreted by the compiler at compile time. The interpretation +happens in the environment of the compilation target and not the host. So +`usize` is `32` bits if you are compiling against a `32` bit system, irrelevant +of whether you are building on a `64` bit or a `32` bit system. If a const function is called outside a [const context], it is indistinguishable from any other function. You can freely do anything with a const function that @@ -214,7 +215,9 @@ are all permitted. This rule also applies to type parameters of impl blocks that - contain const methods + contain const methods. + + This does not apply to tuple struct and tuple variant constructors. * Arithmetic and comparison operators on integers * All boolean operators except for `&&` and `||` which are banned since @@ -345,12 +348,40 @@ > Note: Except for lints, it is idiomatic to only use outer attributes on > function items. -The attributes that have meaning on a function are [`cfg`], [`deprecated`], +The attributes that have meaning on a function are [`cfg`], [`cfg_attr`], [`deprecated`], [`doc`], [`export_name`], [`link_section`], [`no_mangle`], [the lint check attributes], [`must_use`], [the procedural macro attributes], [the testing attributes], and [the optimization hint attributes]. Functions also accept attributes macros. +## Attributes on function parameters + +[Outer attributes][attributes] are allowed on function parameters and the +permitted [built-in attributes] are restricted to `cfg`, `cfg_attr`, `allow`, +`warn`, `deny`, and `forbid`. + +```rust +fn len( + #[cfg(windows)] slice: &[u16], + #[cfg(not(windows))] slice: &[u8], +) -> usize { + slice.len() +} +``` + +Inert helper attributes used by procedural macro attributes applied to items are also +allowed but be careful to not include these inert attributes in your final `TokenStream`. + +For example, the following code defines an inert `some_inert_attribute` attribute that +is not formally defined anywhere and the `some_proc_macro_attribute` procedural macro is +responsible for detecting its presence and removing it from the output token stream. + +```rust,ignore +#[some_proc_macro_attribute] +fn foo_oof(#[some_inert_attribute] arg: u8) { +} +``` + [IDENTIFIER]: ../identifiers.md [RAW_STRING_LITERAL]: ../tokens.md#raw-string-literals [STRING_LITERAL]: ../tokens.md#string-literals @@ -359,7 +390,10 @@ [_Pattern_]: ../patterns.md [_Type_]: ../types.md#type-expressions [_WhereClause_]: generics.md#where-clauses +[_OuterAttribute_]: ../attributes.md [const context]: ../const_eval.md#const-context +[tuple struct]: structs.md +[tuple variant]: enumerations.md [external block]: external-blocks.md [path]: ../paths.md [block]: ../expressions/block-expr.md @@ -368,7 +402,8 @@ [*function item type*]: ../types/function-item.md [Trait]: traits.md [attributes]: ../attributes.md -[`cfg`]: ../conditional-compilation.md +[`cfg`]: ../conditional-compilation.md#the-cfg-attribute +[`cfg_attr`]: ../conditional-compilation.md#the-cfg_attr-attribute [the lint check attributes]: ../attributes/diagnostics.md#lint-check-attributes [the procedural macro attributes]: ../procedural-macros.md [the testing attributes]: ../attributes/testing.md @@ -383,3 +418,4 @@ [`link_section`]: ../abi.md#the-link_section-attribute [`no_mangle`]: ../abi.md#the-no_mangle-attribute [external_block_abi]: external-blocks.md#abi +[built-in attributes]: ../attributes.html#built-in-attributes-index
diff --git a/src/doc/reference/src/items/generics.md b/src/doc/reference/src/items/generics.md index 074b5cf..6dc8f01 100644 --- a/src/doc/reference/src/items/generics.md +++ b/src/doc/reference/src/items/generics.md
@@ -92,10 +92,11 @@ This example shows using a custom derive attribute to modify the meaning of a generic parameter. -```ignore +```rust,ignore // Assume that the derive for MyFlexibleClone declared `my_flexible_clone` as // an attribute it understands. -#[derive(MyFlexibleClone)] struct Foo<#[my_flexible_clone(unbounded)] H> { +#[derive(MyFlexibleClone)] +struct Foo<#[my_flexible_clone(unbounded)] H> { a: *const H } ```
diff --git a/src/doc/reference/src/items/traits.md b/src/doc/reference/src/items/traits.md index e835a60..dea8eca 100644 --- a/src/doc/reference/src/items/traits.md +++ b/src/doc/reference/src/items/traits.md
@@ -38,7 +38,7 @@ > _TraitFunctionParam_ (`,` _TraitFunctionParam_)<sup>\*</sup> `,`<sup>?</sup> > > _TraitFunctionParam_<sup>[†](#parameter-patterns)</sup> :\ -> ( [_Pattern_] `:` )<sup>?</sup> [_Type_] +> [_OuterAttribute_]<sup>\*</sup> ( [_Pattern_] `:` )<sup>?</sup> [_Type_] > > _TraitConst_ :\ > `const` [IDENTIFIER] `:` [_Type_] ( `=` [_Expression_] )<sup>?</sup> `;`
diff --git a/src/doc/reference/src/items/unions.md b/src/doc/reference/src/items/unions.md index 22c40df..d63bf1e 100644 --- a/src/doc/reference/src/items/unions.md +++ b/src/doc/reference/src/items/unions.md
@@ -119,8 +119,8 @@ fn is_zero(v: Value) -> bool { unsafe { match v { - Value { tag: I, u: U { i: 0 } } => true, - Value { tag: F, u: U { f: 0.0 } } => true, + Value { tag: Tag::I, u: U { i: 0 } } => true, + Value { tag: Tag::F, u: U { f: num } } if num == 0.0 => true, _ => false, } }
diff --git a/src/doc/reference/src/keywords.md b/src/doc/reference/src/keywords.md index 4eac070..9df5b2a 100644 --- a/src/doc/reference/src/keywords.md +++ b/src/doc/reference/src/keywords.md
@@ -60,6 +60,8 @@ The following keywords were added beginning in the 2018 edition. > **<sup>Lexer 2018+</sup>**\ +> KW_ASYNC : `async`\ +> KW_AWAIT : `await`\ > KW_DYN : `dyn` ## Reserved keywords @@ -86,8 +88,6 @@ The following keywords are reserved beginning in the 2018 edition. > **<sup>Lexer 2018+</sup>**\ -> KW_ASYNC : `async`\ -> KW_AWAIT : `await`\ > KW_TRY : `try` ## Weak keywords
diff --git a/src/doc/reference/src/macros-by-example.md b/src/doc/reference/src/macros-by-example.md index 8cae03a..fd7c335 100644 --- a/src/doc/reference/src/macros-by-example.md +++ b/src/doc/reference/src/macros-by-example.md
@@ -128,7 +128,7 @@ * `ident`: an [IDENTIFIER_OR_KEYWORD] * `path`: a [_TypePath_] style path * `tt`: a [_TokenTree_] (a single [token] or tokens in matching delimiters `()`, `[]`, or `{}`) - * `meta`: a [_MetaItem_], the contents of an attribute + * `meta`: an [_Attr_], the contents of an attribute * `lifetime`: a [LIFETIME_TOKEN] * `vis`: a possibly empty [_Visibility_] qualifier * `literal`: matches `-`<sup>?</sup>[_LiteralExpression_] @@ -477,12 +477,12 @@ [LIFETIME_TOKEN]: tokens.md#lifetimes-and-loop-labels [Metavariables]: #metavariables [Repetitions]: #repetitions +[_Attr_]: attributes.md [_BlockExpression_]: expressions/block-expr.md [_DelimTokenTree_]: macros.md [_Expression_]: expressions.md [_Item_]: items.md [_LiteralExpression_]: expressions/literal-expr.md -[_MetaItem_]: attributes.md#meta-item-attribute-syntax [_MetaListIdents_]: attributes.md#meta-item-attribute-syntax [_Pattern_]: patterns.md [_Statement_]: statements.md
diff --git a/src/doc/reference/src/macros.md b/src/doc/reference/src/macros.md index cdac0b2..f1ca1a9 100644 --- a/src/doc/reference/src/macros.md +++ b/src/doc/reference/src/macros.md
@@ -37,6 +37,7 @@ * [Types] * [Items] including [associated items] * [`macro_rules`] transcribers +* [External blocks] When used as an item or a statement, the _MacroInvocationSemi_ form is used where a semicolon is required at the end when not using curly braces. @@ -99,3 +100,4 @@ [statements]: statements.md [types]: types.md [visibility qualifiers]: visibility-and-privacy.md +[External blocks]: items/external-blocks.md
diff --git a/src/doc/reference/src/procedural-macros.md b/src/doc/reference/src/procedural-macros.md index 9818b26..ff3f6df 100644 --- a/src/doc/reference/src/procedural-macros.md +++ b/src/doc/reference/src/procedural-macros.md
@@ -75,9 +75,7 @@ These macros are defined by a [public] [function] with the `proc_macro` [attribute] and a signature of `(TokenStream) -> TokenStream`. The input [`TokenStream`] is what is inside the delimiters of the macro invocation and the -output [`TokenStream`] replaces the entire macro invocation. It may contain an -arbitrary number of [items]. These macros cannot expand to syntax that defines -new `macro_rules` style macros. +output [`TokenStream`] replaces the entire macro invocation. For example, the following macro definition ignores its input and outputs a function `answer` into its scope. @@ -105,11 +103,12 @@ } ``` -These macros are only invokable in [modules]. They cannot even be invoked to -create [item declaration statements]. Furthermore, they must either be invoked -with curly braces and no semicolon or a different delimiter followed by a -semicolon. For example, `make_answer` from the previous example can be invoked -as `make_answer!{}`, `make_answer!();` or `make_answer![];`. +Function-like procedural macros may expand to a [type] or any number of +[items], including [`macro_rules`] definitions. They may be invoked in a [type +expression], [item] position (except as a [statement]), including items in +[`extern` blocks], inherent and trait [implementations], and [trait +definitions]. They cannot be used in a [statement], [expression], or +[pattern]. ### Derive macros @@ -192,7 +191,9 @@ ### Attribute macros -*Attribute macros* define new [attributes] which can be attached to [items]. +*Attribute macros* define new [outer attributes][attributes] which can be +attached to [items], including items in [`extern` blocks], inherent and trait +[implementations], and [trait definitions]. Attribute macros are defined by a [public] [function] with the `proc_macro_attribute` [attribute] that has a signature of `(TokenStream, @@ -201,8 +202,7 @@ the attribute is written as a bare attribute name, the attribute [`TokenStream`] is empty. The second [`TokenStream`] is the rest of the [item] including other [attributes] on the [item]. The returned [`TokenStream`] -replaces the [item] with an arbitrary number of [items]. These macros cannot -expand to syntax that defines new `macro_rules` style macros. +replaces the [item] with an arbitrary number of [items]. For example, this attribute macro takes the input stream and returns it as is, effectively being the no-op of attributes. @@ -266,28 +266,35 @@ // out: item: "fn invoke4() {}" ``` +[Attribute macros]: #attribute-macros +[Cargo's build scripts]: ../cargo/reference/build-scripts.html +[Derive macros]: #derive-macros +[Function-like macros]: #function-like-procedural-macros [`TokenStream`]: ../proc_macro/struct.TokenStream.html [`TokenStream`s]: ../proc_macro/struct.TokenStream.html [`compile_error`]: ../std/macro.compile_error.html [`derive` attribute]: attributes/derive.md +[`extern` blocks]: items/external-blocks.md +[`macro_rules`]: macros-by-example.md [`proc_macro` crate]: ../proc_macro/index.html -[Cargo's build scripts]: ../cargo/reference/build-scripts.html -[Derive macros]: #derive-macros -[Attribute macros]: #attribute-macros -[Function-like macros]: #function-like-procedural-macros [attribute]: attributes.md [attributes]: attributes.md [block]: expressions/block-expr.md [crate type]: linkage.md [derive macro helper attributes]: #derive-macro-helper-attributes [enum]: items/enumerations.md +[expression]: expressions.md +[function]: items/functions.md +[implementations]: items/implementations.md [inert]: attributes.md#active-and-inert-attributes [item]: items.md -[item declaration statements]: statements.md#item-declarations [items]: items.md -[function]: items/functions.md [module]: items/modules.md -[modules]: items/modules.md +[pattern]: patterns.md [public]: visibility-and-privacy.md +[statement]: statements.md [struct]: items/structs.md +[trait definitions]: items/traits.md +[type expression]: types.md#type-expressions +[type]: types.md [union]: items/unions.md
diff --git a/src/doc/reference/src/type-layout.md b/src/doc/reference/src/type-layout.md index b0cc71a..57e801e 100644 --- a/src/doc/reference/src/type-layout.md +++ b/src/doc/reference/src/type-layout.md
@@ -34,22 +34,17 @@ The size of most primitives is given in this table. -Type | `size_of::<Type>()` -- | - | - -bool | 1 -u8 | 1 -u16 | 2 -u32 | 4 -u64 | 8 -u128 | 16 -i8 | 1 -i16 | 2 -i32 | 4 -i64 | 8 -i128 | 16 -f32 | 4 -f64 | 8 -char | 4 +| Type | `size_of::<Type>()`| +|-- |-- | +| `bool` | 1 | +| `u8` / `i8` | 1 | +| `u16` / `i16` | 2 | +| `u32` / `i32` | 4 | +| `u64` / `i64` | 8 | +| `u128` / `i128` | 16 | +| `f32` | 4 | +| `f64` | 8 | +| `char` | 4 | `usize` and `isize` have a size big enough to contain every address on the target platform. For example, on a 32 bit target, this is 4 bytes and on a 64
diff --git a/src/doc/reference/src/types/function-pointer.md b/src/doc/reference/src/types/function-pointer.md index 88ef50c..912ee93 100644 --- a/src/doc/reference/src/types/function-pointer.md +++ b/src/doc/reference/src/types/function-pointer.md
@@ -15,10 +15,10 @@ > _MaybeNamedParam_ ( `,` _MaybeNamedParam_ )<sup>\*</sup> `,`<sup>?</sup> > > _MaybeNamedParam_ :\ -> ( ( [IDENTIFIER] | `_` ) `:` )<sup>?</sup> [_Type_] +> [_OuterAttribute_]<sup>\*</sup> ( ( [IDENTIFIER] | `_` ) `:` )<sup>?</sup> [_Type_] > > _MaybeNamedFunctionParametersVariadic_ :\ -> ( _MaybeNamedParam_ `,` )<sup>\*</sup> _MaybeNamedParam_ `,` `...` +> ( _MaybeNamedParam_ `,` )<sup>\*</sup> _MaybeNamedParam_ `,` [_OuterAttribute_]<sup>\*</sup> `...` Function pointer types, written using the `fn` keyword, refer to a function whose identity is not necessarily known at compile-time. They can be created @@ -44,13 +44,20 @@ x = bo(5,7); ``` +## Attributes on function pointer parameters + +Attributes on function pointer parameters follow the same rules and +restrictions as [regular function parameters]. + [IDENTIFIER]: ../identifiers.md [_ForLifetimes_]: ../items/generics.md#where-clauses [_FunctionQualifiers_]: ../items/functions.md [_TypeNoBounds_]: ../types.md#type-expressions [_Type_]: ../types.md#type-expressions +[_OuterAttribute_]: ../attributes.md [`extern`]: ../items/external-blocks.md [closures]: closure.md [extern function]: ../items/functions.md#extern-function-qualifier [function items]: function-item.md [unsafe function]: ../unsafe-functions.md +[regular function parameters]: ../items/functions.md#attributes-on-function-parameters \ No newline at end of file
diff --git a/src/doc/reference/theme/header.hbs b/src/doc/reference/theme/header.hbs deleted file mode 100644 index 85db69f..0000000 --- a/src/doc/reference/theme/header.hbs +++ /dev/null
@@ -1,8 +0,0 @@ -<header><p class="warning"> - For now, this reference is a best-effort document. We strive for validity - and completeness, but are not yet there. In the future, the docs and lang - teams will work together to figure out how best to do this. Until then, this - is a best-effort attempt. If you find something wrong or missing, file an - <a href="https://github.com/rust-lang-nursery/reference/issues">issue</a> or - send in a pull request. -</p></header> \ No newline at end of file
diff --git a/src/doc/reference/theme/reference.css b/src/doc/reference/theme/reference.css index e146305..06ea927 100644 --- a/src/doc/reference/theme/reference.css +++ b/src/doc/reference/theme/reference.css
@@ -1,58 +1,3 @@ -/* These selectors moves things around to make space for the warning on the - top of each page. Get rid of it when the warning goes away. */ -.page-wrapper > .nav-chapters { - /* add height for warning content & margin */ - top: 120px; -} - -.sidebar-visible .content { - top: 120px; -} - -.nav-chapters { - top: 120px; -} - -p.warning { - background-color: rgb(242, 222, 222); - border-bottom-color: rgb(238, 211, 215); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - border-bottom-style: solid; - border-bottom-width: 0.666667px; - border-image-outset: 0 0 0 0; - border-image-repeat: stretch stretch; - border-image-slice: 100% 100% 100% 100%; - border-image-source: none; - border-image-width: 1 1 1 1; - border-left-color: rgb(238, 211, 215); - border-left-style: solid; - border-left-width: 0.666667px; - border-right-color: rgb(238, 211, 215); - border-right-style: solid; - border-right-width: 0.666667px; - border-top-color: rgb(238, 211, 215); - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-top-style: solid; - border-top-width: 0.666667px; - color: rgb(185, 74, 72); - margin-bottom: 0px; - margin-left: 0px; - margin-right: 0px; - margin-top: 30px; - padding-bottom: 8px; - padding-left: 14px; - padding-right: 35px; - padding-top: 8px; -} -p.warning strong { - color: rgb(185, 74, 72) -} -p.warning a { - color: rgb(0, 136, 204) -} - /* .parenthetical class used to keep e.g. "less-than symbol (<)" from wrapping the end parenthesis onto its own line. Use in a span between the last word and
diff --git a/src/doc/rust-by-example/src/SUMMARY.md b/src/doc/rust-by-example/src/SUMMARY.md index cd6bbc7..85711ff 100644 --- a/src/doc/rust-by-example/src/SUMMARY.md +++ b/src/doc/rust-by-example/src/SUMMARY.md
@@ -69,7 +69,7 @@ - [As output parameters](fn/closures/output_parameters.md) - [Examples in `std`](fn/closures/closure_examples.md) - [Iterator::any](fn/closures/closure_examples/iter_any.md) - - [Iterator::find](fn/closures/closure_examples/iter_find.md) + - [Searching through iterators](fn/closures/closure_examples/iter_find.md) - [Higher Order Functions](fn/hof.md) - [Diverging functions](fn/diverging.md) @@ -139,8 +139,8 @@ - [Iterators](trait/iter.md) - [`impl Trait`](trait/impl_trait.md) - [Clone](trait/clone.md) - - [Supertraits](traits/supertraits.md) - - [Disambiguating overlapping traits](traits/disambiguating.md) + - [Supertraits](trait/supertraits.md) + - [Disambiguating overlapping traits](trait/disambiguating.md) - [macro_rules!](macros.md) - [Syntax](macros/syntax.md)
diff --git a/src/doc/rust-by-example/src/cargo/build_scripts.md b/src/doc/rust-by-example/src/cargo/build_scripts.md index c5da00d..6db3afe 100644 --- a/src/doc/rust-by-example/src/cargo/build_scripts.md +++ b/src/doc/rust-by-example/src/cargo/build_scripts.md
@@ -1,7 +1,7 @@ # Build Scripts -Sometimes a normal build from cargo is not enough. Perhaps your crate needs some -pre-requisites before cargo will successfully compile, things like code +Sometimes a normal build from `cargo` is not enough. Perhaps your crate needs +some pre-requisites before `cargo` will successfully compile, things like code generation, or some native code that needs to be compiled. To solve this problem we have build scripts that Cargo can run. @@ -20,7 +20,7 @@ ## How to use a build script The build script is simply another Rust file that will be compiled and invoked -prior to compiling anything else in the package. Hence it can be used to fulfil +prior to compiling anything else in the package. Hence it can be used to fulfill pre-requisites of your crate. Cargo provides the script with inputs via environment variables [specified @@ -29,10 +29,11 @@ The script provides output via stdout. All lines printed are written to `target/debug/build/<pkg>/output`. Further, lines prefixed with `cargo:` will be interpreted by Cargo directly and hence can be used to define parameters for the -packages compilation. +package's compilation. -For further specification and examples have a read of the [cargo specification]. +For further specification and examples have a read of the +[Cargo specification][cargo_specification]. [specified here]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts -[cargo specification]: https://doc.rust-lang.org/cargo/reference/build-scripts.html \ No newline at end of file +[cargo_specification]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
diff --git a/src/doc/rust-by-example/src/cargo/deps.md b/src/doc/rust-by-example/src/cargo/deps.md index 21db4c2..7403a2a 100644 --- a/src/doc/rust-by-example/src/cargo/deps.md +++ b/src/doc/rust-by-example/src/cargo/deps.md
@@ -15,10 +15,10 @@ cargo new --lib foo ``` -For the rest of this chapter, I will assume we are making a binary, rather than +For the rest of this chapter, let's assume we are making a binary, rather than a library, but all of the concepts are the same. -After the above commands, you should see something like this: +After the above commands, you should see a file hierarchy like this: ```txt foo @@ -40,7 +40,7 @@ [dependencies] ``` -The `name` field under `package` determines the name of the project. This is +The `name` field under `[package]` determines the name of the project. This is used by `crates.io` if you publish the crate (more later). It is also the name of the output binary when you compile. @@ -49,14 +49,14 @@ The `authors` field is a list of authors used when publishing the crate. -The `dependencies` section lets you add a dependency for your project. +The `[dependencies]` section lets you add dependencies for your project. -For example, suppose that I want my program to have a great CLI. You can find +For example, suppose that we want our program to have a great CLI. You can find lots of great packages on [crates.io](https://crates.io) (the official Rust package registry). One popular choice is [clap](https://crates.io/crates/clap). As of this writing, the most recent published version of `clap` is `2.27.1`. To add a dependency to our program, we can simply add the following to our -`Cargo.toml` under `dependencies`: `clap = "2.27.1"`. And of course, `extern +`Cargo.toml` under `[dependencies]`: `clap = "2.27.1"`. And of course, `extern crate clap` in `main.rs`, just like normal. And that's it! You can start using `clap` in your program.
diff --git a/src/doc/rust-by-example/src/conversion/string.md b/src/doc/rust-by-example/src/conversion/string.md index 2bec02f..ab14521 100644 --- a/src/doc/rust-by-example/src/conversion/string.md +++ b/src/doc/rust-by-example/src/conversion/string.md
@@ -29,16 +29,16 @@ ## Parsing a String One of the more common types to convert a string into is a number. The idiomatic -approach to this is to use the [`parse`] function and provide the type for the -function to parse the string value into, this can be done either without type -inference or using the 'turbofish' syntax. +approach to this is to use the [`parse`] function and either to arrange for +type inference or to specify the type to parse using the 'turbofish' syntax. +Both alternatives are shown in the following example. This will convert the string into the type specified so long as the [`FromStr`] trait is implemented for that type. This is implemented for numerous types within the standard library. To obtain this functionality on a user defined type simply implement the [`FromStr`] trait for that type. -```rust +```rust,editable fn main() { let parsed: i32 = "5".parse().unwrap(); let turbo_parsed = "10".parse::<i32>().unwrap();
diff --git a/src/doc/rust-by-example/src/custom_types/enum.md b/src/doc/rust-by-example/src/custom_types/enum.md index 8580ce9..e861df9 100644 --- a/src/doc/rust-by-example/src/custom_types/enum.md +++ b/src/doc/rust-by-example/src/custom_types/enum.md
@@ -55,7 +55,7 @@ ## Type aliases -If you use a type alias, you can refer to each enum variant via its alias. +If you use a type alias, you can refer to each enum variant via its alias. This might be useful if the enum's name is too long or too generic, and you want to rename it. @@ -93,16 +93,17 @@ } ``` -To learn more about enums and type aliases, you can read the +To learn more about enums and type aliases, you can read the [stabilization report][aliasreport] from when this feature was stabilized into -Rust. +Rust. ### See also: -[`match`][match], [`fn`][fn], and [`String`][str], [] +[`match`][match], [`fn`][fn], and [`String`][str], ["Type alias enum variants" RFC][type_alias_rfc] [c_struct]: https://en.wikipedia.org/wiki/Struct_(C_programming_language) [match]: ../flow_control/match.md [fn]: ../fn.md [str]: ../std/str.md [aliasreport]: https://github.com/rust-lang/rust/pull/61682/#issuecomment-502472847 +[type_alias_rfc]: https://rust-lang.github.io/rfcs/2338-type-alias-enum-variants.html
diff --git a/src/doc/rust-by-example/src/custom_types/structs.md b/src/doc/rust-by-example/src/custom_types/structs.md index 0b620bf..e863e0e 100644 --- a/src/doc/rust-by-example/src/custom_types/structs.md +++ b/src/doc/rust-by-example/src/custom_types/structs.md
@@ -10,6 +10,7 @@ ```rust,editable #[derive(Debug)] struct Person<'a> { + // The 'a defines a lifetime name: &'a str, age: u8, } @@ -29,8 +30,10 @@ // Structs can be reused as fields of another struct #[allow(dead_code)] struct Rectangle { - p1: Point, - p2: Point, + // A rectangle can be specified by where the top left and bottom right + // corners are in space. + top_left: Point, + bottom_right: Point, } fn main() { @@ -44,23 +47,26 @@ // Instantiate a `Point` - let point: Point = Point { x: 0.3, y: 0.4 }; + let point: Point = Point { x: 10.3, y: 0.4 }; // Access the fields of the point println!("point coordinates: ({}, {})", point.x, point.y); - // Make a new point by using struct update syntax to use the fields of our other one - let new_point = Point { x: 0.1, ..point }; - // `new_point.y` will be the same as `point.y` because we used that field from `point` - println!("second point: ({}, {})", new_point.x, new_point.y); + // Make a new point by using struct update syntax to use the fields of our + // other one + let bottom_right = Point { x: 5.2, ..point }; + + // `bottom_right.y` will be the same as `point.y` because we used that field + // from `point` + println!("second point: ({}, {})", bottom_right.x, bottom_right.y); // Destructure the point using a `let` binding - let Point { x: my_x, y: my_y } = point; + let Point { x: top_edge, y: left_edge } = point; let _rectangle = Rectangle { // struct instantiation is an expression too - p1: Point { x: my_y, y: my_x }, - p2: point, + top_left: Point { x: left_edge, y: top_edge }, + bottom_right: bottom_right, }; // Instantiate a unit struct @@ -81,14 +87,15 @@ ### Activity -1. Add a function `rect_area` which calculates the area of a rectangle (try - using nested destructuring). +1. Add a function `rect_area` which calculates the area of a rectangle (try + using nested destructuring). 2. Add a function `square` which takes a `Point` and a `f32` as arguments, and returns a `Rectangle` with its lower left corner on the point, and a width and height corresponding to the `f32`. ### See also: -[`attributes`][attributes] and [destructuring][destructuring] +[`attributes`][attributes], [lifetime][lifetime] and [destructuring][destructuring] [attributes]: ../attribute.md [c_struct]: https://en.wikipedia.org/wiki/Struct_(C_programming_language) [destructuring]: ../flow_control/match/destructuring.md +[lifetime]: ../scope/lifetime.md
diff --git a/src/doc/rust-by-example/src/error/result.md b/src/doc/rust-by-example/src/error/result.md index 3202b31..d779bfd 100644 --- a/src/doc/rust-by-example/src/error/result.md +++ b/src/doc/rust-by-example/src/error/result.md
@@ -5,8 +5,8 @@ That is, `Result<T, E>` could have one of two outcomes: -* `Ok<T>`: An element `T` was found -* `Err<E>`: An error was found with element `E` +* `Ok(T)`: An element `T` was found +* `Err(E)`: An error was found with element `E` By convention, the expected outcome is `Ok` while the unexpected outcome is `Err`.
diff --git a/src/doc/rust-by-example/src/flow_control/if_let.md b/src/doc/rust-by-example/src/flow_control/if_let.md index a567133..22bdde8 100644 --- a/src/doc/rust-by-example/src/flow_control/if_let.md +++ b/src/doc/rust-by-example/src/flow_control/if_let.md
@@ -103,7 +103,7 @@ Would you like a challenge? Fix the following example to use `if let`: -```rust,editable,ignore +```rust,editable,ignore,mdbook-runnable // This enum purposely doesn't #[derive(PartialEq)], // neither we implement PartialEq for it. That's why comparing Foo::Bar==a fails below. enum Foo {Bar}
diff --git a/src/doc/rust-by-example/src/flow_control/match.md b/src/doc/rust-by-example/src/flow_control/match.md index fb0f56e..02a8067 100644 --- a/src/doc/rust-by-example/src/flow_control/match.md +++ b/src/doc/rust-by-example/src/flow_control/match.md
@@ -15,7 +15,7 @@ // Match several values 2 | 3 | 5 | 7 | 11 => println!("This is a prime"), // Match an inclusive range - 13...19 => println!("A teen"), + 13..=19 => println!("A teen"), // Handle the rest of cases _ => println!("Ain't special"), } @@ -31,4 +31,4 @@ println!("{} -> {}", boolean, binary); } -``` \ No newline at end of file +```
diff --git a/src/doc/rust-by-example/src/flow_control/match/binding.md b/src/doc/rust-by-example/src/flow_control/match/binding.md index e1aa232..0407b37 100644 --- a/src/doc/rust-by-example/src/flow_control/match/binding.md +++ b/src/doc/rust-by-example/src/flow_control/match/binding.md
@@ -15,9 +15,9 @@ match age() { 0 => println!("I'm not born yet I guess"), - // Could `match` 1 ... 12 directly but then what age + // Could `match` 1 ..= 12 directly but then what age // would the child be? Instead, bind to `n` for the - // sequence of 1 .. 12. Now the age can be reported. + // sequence of 1 ..= 12. Now the age can be reported. n @ 1 ..= 12 => println!("I'm a child of age {:?}", n), n @ 13 ..= 19 => println!("I'm a teen of age {:?}", n), // Nothing bound. Return the result.
diff --git a/src/doc/rust-by-example/src/fn/closures/capture.md b/src/doc/rust-by-example/src/fn/closures/capture.md index 3646878..d4fa4ea 100644 --- a/src/doc/rust-by-example/src/fn/closures/capture.md +++ b/src/doc/rust-by-example/src/fn/closures/capture.md
@@ -18,37 +18,51 @@ let color = "green"; - // A closure to print `color` which immediately borrows (`&`) - // `color` and stores the borrow and closure in the `print` - // variable. It will remain borrowed until `print` goes out of - // scope. `println!` only requires `by reference` so it doesn't + // A closure to print `color` which immediately borrows (`&`) `color` and + // stores the borrow and closure in the `print` variable. It will remain + // borrowed until `print` is used the last time. + // + // `println!` only requires arguments by immutable reference so it doesn't // impose anything more restrictive. let print = || println!("`color`: {}", color); // Call the closure using the borrow. print(); + + // `color` can be borrowed immutably again, because the closure only holds + // an immutable reference to `color`. + let _reborrow = &color; print(); - let mut count = 0; + // A move or reborrow is allowed after the final use of `print` + let _color_moved = color; - // A closure to increment `count` could take either `&mut count` - // or `count` but `&mut count` is less restrictive so it takes - // that. Immediately borrows `count`. + + let mut count = 0; + // A closure to increment `count` could take either `&mut count` or `count` + // but `&mut count` is less restrictive so it takes that. Immediately + // borrows `count`. // - // A `mut` is required on `inc` because a `&mut` is stored inside. - // Thus, calling the closure mutates the closure which requires - // a `mut`. + // A `mut` is required on `inc` because a `&mut` is stored inside. Thus, + // calling the closure mutates the closure which requires a `mut`. let mut inc = || { count += 1; println!("`count`: {}", count); }; - // Call the closure. - inc(); + // Call the closure using a mutable borrow. inc(); - //let _reborrow = &mut count; + // The closure still mutably borrows `count` because it is called later. + // An attempt to reborrow will lead to an error. + // let _reborrow = &count; // ^ TODO: try uncommenting this line. + inc(); + + // The closure no longer needs to borrow `&mut count`. Therefore, it is + // possible to reborrow without an error + let _count_reborrowed = &mut count; + // A non-copy type. let movable = Box::new(3); @@ -64,7 +78,7 @@ // `consume` consumes the variable so this can only be called once. consume(); - //consume(); + // consume(); // ^ TODO: Try uncommenting this line. } ``` @@ -82,7 +96,7 @@ println!("{}", contains(&1)); println!("{}", contains(&4)); - // `println!("There're {} elements in vec", haystack.len());` + // println!("There're {} elements in vec", haystack.len()); // ^ Uncommenting above line will result in compile-time error // because borrow checker doesn't allow re-using variable after it // has been moved.
diff --git a/src/doc/rust-by-example/src/fn/closures/closure_examples/iter_find.md b/src/doc/rust-by-example/src/fn/closures/closure_examples/iter_find.md index 77ddad2..9eb5072 100644 --- a/src/doc/rust-by-example/src/fn/closures/closure_examples/iter_find.md +++ b/src/doc/rust-by-example/src/fn/closures/closure_examples/iter_find.md
@@ -1,8 +1,8 @@ -# Iterator::find +# Searching through iterators -`Iterator::find` is a function which when passed an iterator, will return -the first element which satisfies the predicate as an `Option`. Its -signature: +`Iterator::find` is a function which iterates over an iterator and searches for the +first value which satisfies some condition. If none of the values satisfy the +condition, it returns `None`. Its signature: ```rust,ignore pub trait Iterator { @@ -29,9 +29,11 @@ // `into_iter()` for vecs yields `i32`. let mut into_iter = vec2.into_iter(); - // A reference to what is yielded is `&&i32`. Destructure to `i32`. + // `iter()` for vecs yields `&i32`, and we want to reference one of its + // items, so we have to destructure `&&i32` to `i32` println!("Find 2 in vec1: {:?}", iter .find(|&&x| x == 2)); - // A reference to what is yielded is `&i32`. Destructure to `i32`. + // `into_iter()` for vecs yields `i32`, and we want to reference one of + // its items, so we have to destructure `&i32` to `i32` println!("Find 2 in vec2: {:?}", into_iter.find(| &x| x == 2)); let array1 = [1, 2, 3]; @@ -44,8 +46,33 @@ } ``` +`Iterator::find` gives you a reference to the item. But if you want the _index_ of the +item, use `Iterator::position`. + +```rust,editable +fn main() { + let vec = vec![1, 9, 3, 3, 13, 2]; + + let index_of_first_even_number = vec.iter().position(|x| x % 2 == 0); + assert_eq!(index_of_first_even_number, Some(5)); + + + let index_of_first_negative_number = vec.iter().position(|x| x < &0); + assert_eq!(index_of_first_negative_number, None); +} +``` + ### See also: [`std::iter::Iterator::find`][find] +[`std::iter::Iterator::find_map`][find_map] + +[`std::iter::Iterator::position`][position] + +[`std::iter::Iterator::rposition`][rposition] + [find]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.find +[find_map]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.find_map +[position]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.position +[rposition]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.rposition
diff --git a/src/doc/rust-by-example/src/fn/closures/input_parameters.md b/src/doc/rust-by-example/src/fn/closures/input_parameters.md index c97a4d1..be36c6d 100644 --- a/src/doc/rust-by-example/src/fn/closures/input_parameters.md +++ b/src/doc/rust-by-example/src/fn/closures/input_parameters.md
@@ -1,33 +1,34 @@ # As input parameters -While Rust chooses how to capture variables on the fly mostly without type -annotation, this ambiguity is not allowed when writing functions. When -taking a closure as an input parameter, the closure's complete type must be -annotated using one of a few `traits`. In order of decreasing restriction, +While Rust chooses how to capture variables on the fly mostly without type +annotation, this ambiguity is not allowed when writing functions. When +taking a closure as an input parameter, the closure's complete type must be +annotated using one of a few `traits`. In order of decreasing restriction, they are: * `Fn`: the closure captures by reference (`&T`) * `FnMut`: the closure captures by mutable reference (`&mut T`) * `FnOnce`: the closure captures by value (`T`) -On a variable-by-variable basis, the compiler will capture variables in the -least restrictive manner possible. +On a variable-by-variable basis, the compiler will capture variables in the +least restrictive manner possible. -For instance, consider a parameter annotated as `FnOnce`. This specifies -that the closure *may* capture by `&T`, `&mut T`, or `T`, but the compiler -will ultimately choose based on how the captured variables are used in the +For instance, consider a parameter annotated as `FnOnce`. This specifies +that the closure *may* capture by `&T`, `&mut T`, or `T`, but the compiler +will ultimately choose based on how the captured variables are used in the closure. -This is because if a move is possible, then any type of borrow should also -be possible. Note that the reverse is not true. If the parameter is -annotated as `Fn`, then capturing variables by `&mut T` or `T` are not +This is because if a move is possible, then any type of borrow should also +be possible. Note that the reverse is not true. If the parameter is +annotated as `Fn`, then capturing variables by `&mut T` or `T` are not allowed. -In the following example, try swapping the usage of `Fn`, `FnMut`, and +In the following example, try swapping the usage of `Fn`, `FnMut`, and `FnOnce` to see what happens: ```rust,editable // A function which takes a closure as an argument and calls it. +// <F> denotes that F is a "Generic type parameter" fn apply<F>(f: F) where // The closure takes no input and returns nothing. F: FnOnce() { @@ -81,9 +82,11 @@ ### See also: -[`std::mem::drop`][drop], [`Fn`][fn], [`FnMut`][fnmut], and [`FnOnce`][fnonce] +[`std::mem::drop`][drop], [`Fn`][fn], [`FnMut`][fnmut], [Generics][generics], [where][where] and [`FnOnce`][fnonce] [drop]: https://doc.rust-lang.org/std/mem/fn.drop.html [fn]: https://doc.rust-lang.org/std/ops/trait.Fn.html [fnmut]: https://doc.rust-lang.org/std/ops/trait.FnMut.html [fnonce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html +[generics]: ../../generics.md +[where]: ../../generics/where.md
diff --git a/src/doc/rust-by-example/src/fn/closures/output_parameters.md b/src/doc/rust-by-example/src/fn/closures/output_parameters.md index 4ac4dd0..6cad173 100644 --- a/src/doc/rust-by-example/src/fn/closures/output_parameters.md +++ b/src/doc/rust-by-example/src/fn/closures/output_parameters.md
@@ -5,13 +5,11 @@ closure types are, by definition, unknown, so we have to use `impl Trait` to return them. -The valid traits for returns are slightly different than before: +The valid traits for returning a closure are: -* `Fn`: normal -* `FnMut`: normal -* `FnOnce`: There are some unusual things at play here, so the [`FnBox`][fnbox] - type is currently needed, and is unstable. This is expected to change in - the future. +* `Fn` +* `FnMut` +* `FnOnce` Beyond this, the `move` keyword must be used, which signals that all captures occur by value. This is required because any captures by reference would be @@ -31,12 +29,20 @@ move || println!("This is a: {}", text) } +fn create_fnonce() -> impl FnOnce() { + let text = "FnOnce".to_owned(); + + move || println!("This is a: {}", text) +} + fn main() { let fn_plain = create_fn(); let mut fn_mut = create_fnmut(); + let fn_once = create_fnonce(); fn_plain(); fn_mut(); + fn_once(); } ``` @@ -46,6 +52,5 @@ [fn]: https://doc.rust-lang.org/std/ops/trait.Fn.html [fnmut]: https://doc.rust-lang.org/std/ops/trait.FnMut.html -[fnbox]: https://doc.rust-lang.org/std/boxed/trait.FnBox.html [generics]: ../../generics.md [impltrait]: ../../trait/impl_trait.md
diff --git a/src/doc/rust-by-example/src/macros/dry.md b/src/doc/rust-by-example/src/macros/dry.md index 7333d0e..ca01527 100644 --- a/src/doc/rust-by-example/src/macros/dry.md +++ b/src/doc/rust-by-example/src/macros/dry.md
@@ -10,7 +10,7 @@ macro_rules! assert_equal_len { // The `tt` (token tree) designator is used for // operators and tokens. - ($a:ident, $b:ident, $func:ident, $op:tt) => { + ($a:expr, $b:expr, $func:ident, $op:tt) => { assert!($a.len() == $b.len(), "{:?}: dimension mismatch: {:?} {:?} {:?}", stringify!($func),
diff --git a/src/doc/rust-by-example/src/primitives/array.md b/src/doc/rust-by-example/src/primitives/array.md index fe4bee9..f612c1d 100644 --- a/src/doc/rust-by-example/src/primitives/array.md +++ b/src/doc/rust-by-example/src/primitives/array.md
@@ -42,6 +42,9 @@ analyze_slice(&xs); // Slices can point to a section of an array + // They are of the form [starting_index..ending_index] + // starting_index is the first position in the slice + // ending_index is one more than the last position in the slice println!("borrow a section of the array as a slice"); analyze_slice(&ys[1 .. 4]);
diff --git a/src/doc/rust-by-example/src/scope/borrow/alias.md b/src/doc/rust-by-example/src/scope/borrow/alias.md index 2a12e60..9cfd5fe 100644 --- a/src/doc/rust-by-example/src/scope/borrow/alias.md +++ b/src/doc/rust-by-example/src/scope/borrow/alias.md
@@ -1,9 +1,9 @@ # Aliasing Data can be immutably borrowed any number of times, but while immutably -borrowed, the original data can't be mutably borrowed. On the other hand, -only *one* mutable borrow is allowed at a time. The original data can be -borrowed again only *after* the mutable reference goes out of scope. +borrowed, the original data can't be mutably borrowed. On the other hand, only +*one* mutable borrow is allowed at a time. The original data can be borrowed +again only *after* the mutable reference has been used for the last time. ```rust,editable struct Point { x: i32, y: i32, z: i32 } @@ -11,49 +11,48 @@ fn main() { let mut point = Point { x: 0, y: 0, z: 0 }; - { - let borrowed_point = &point; - let another_borrow = &point; - - // Data can be accessed via the references and the original owner - println!("Point has coordinates: ({}, {}, {})", - borrowed_point.x, another_borrow.y, point.z); - - // Error! Can't borrow `point` as mutable because it's currently - // borrowed as immutable. - //let mutable_borrow = &mut point; - // TODO ^ Try uncommenting this line - - // Immutable references go out of scope - } - - { - let mutable_borrow = &mut point; - - // Change data via mutable reference - mutable_borrow.x = 5; - mutable_borrow.y = 2; - mutable_borrow.z = 1; - - // Error! Can't borrow `point` as immutable because it's currently - // borrowed as mutable. - //let y = &point.y; - // TODO ^ Try uncommenting this line - - // Error! Can't print because `println!` takes an immutable reference. - //println!("Point Z coordinate is {}", point.z); - // TODO ^ Try uncommenting this line - - // Ok! Mutable references can be passed as immutable to `println!` - println!("Point has coordinates: ({}, {}, {})", - mutable_borrow.x, mutable_borrow.y, mutable_borrow.z); - - // Mutable reference goes out of scope - } - - // Immutable references to `point` are allowed again let borrowed_point = &point; + let another_borrow = &point; + + // Data can be accessed via the references and the original owner + println!("Point has coordinates: ({}, {}, {})", + borrowed_point.x, another_borrow.y, point.z); + + // Error! Can't borrow `point` as mutable because it's currently + // borrowed as immutable. + // let mutable_borrow = &mut point; + // TODO ^ Try uncommenting this line + + // The borrowed values are used again here + println!("Point has coordinates: ({}, {}, {})", + borrowed_point.x, another_borrow.y, point.z); + + // The immutable references are no longer used for the rest of the code so + // it is possible to reborrow with a mutable reference. + let mutable_borrow = &mut point; + + // Change data via mutable reference + mutable_borrow.x = 5; + mutable_borrow.y = 2; + mutable_borrow.z = 1; + + // Error! Can't borrow `point` as immutable because it's currently + // borrowed as mutable. + // let y = &point.y; + // TODO ^ Try uncommenting this line + + // Error! Can't print because `println!` takes an immutable reference. + // println!("Point Z coordinate is {}", point.z); + // TODO ^ Try uncommenting this line + + // Ok! Mutable references can be passed as immutable to `println!` + println!("Point has coordinates: ({}, {}, {})", + mutable_borrow.x, mutable_borrow.y, mutable_borrow.z); + + // The mutable reference is no longer used for the rest of the code so it + // is possible to reborrow + let new_borrowed_point = &point; println!("Point now has coordinates: ({}, {}, {})", - borrowed_point.x, borrowed_point.y, borrowed_point.z); + new_borrowed_point.x, new_borrowed_point.y, new_borrowed_point.z); } ```
diff --git a/src/doc/rust-by-example/src/std/box.md b/src/doc/rust-by-example/src/std/box.md index 8d45e1a..ebc8ff4 100644 --- a/src/doc/rust-by-example/src/std/box.md +++ b/src/doc/rust-by-example/src/std/box.md
@@ -18,10 +18,12 @@ y: f64, } +// A Rectangle can be specified by where its top left and bottom right +// corners are in space #[allow(dead_code)] struct Rectangle { - p1: Point, - p2: Point, + top_left: Point, + bottom_right: Point, } fn origin() -> Point { @@ -38,14 +40,14 @@ // Stack allocated variables let point: Point = origin(); let rectangle: Rectangle = Rectangle { - p1: origin(), - p2: Point { x: 3.0, y: 4.0 } + top_left: origin(), + bottom_right: Point { x: 3.0, y: -4.0 } }; // Heap allocated rectangle let boxed_rectangle: Box<Rectangle> = Box::new(Rectangle { - p1: origin(), - p2: origin() + top_left: origin(), + bottom_right: Point { x: 3.0, y: -4.0 }, }); // The output of functions can be boxed
diff --git a/src/doc/rust-by-example/src/testing/dev_dependencies.md b/src/doc/rust-by-example/src/testing/dev_dependencies.md index 4c08979..c20decf 100644 --- a/src/doc/rust-by-example/src/testing/dev_dependencies.md +++ b/src/doc/rust-by-example/src/testing/dev_dependencies.md
@@ -1,7 +1,7 @@ # Development dependencies -Sometimes there is a need to have a dependencies for tests (examples, -benchmarks) only. Such dependencies are added to `Cargo.toml` in +Sometimes there is a need to have dependencies for tests (examples, +benchmarks) only. Such dependencies are added to `Cargo.toml` in the `[dev-dependencies]` section. These dependencies are not propagated to other packages which depend on this package.
diff --git a/src/doc/rust-by-example/src/traits/disambiguating.md b/src/doc/rust-by-example/src/trait/disambiguating.md similarity index 100% rename from src/doc/rust-by-example/src/traits/disambiguating.md rename to src/doc/rust-by-example/src/trait/disambiguating.md
diff --git a/src/doc/rust-by-example/src/traits/supertraits.md b/src/doc/rust-by-example/src/trait/supertraits.md similarity index 100% rename from src/doc/rust-by-example/src/traits/supertraits.md rename to src/doc/rust-by-example/src/trait/supertraits.md
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 3cda8d9..b603c7b 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md
@@ -10,9 +10,11 @@ - [Warn-by-default lints](lints/listing/warn-by-default.md) - [Deny-by-default lints](lints/listing/deny-by-default.md) - [Codegen options](codegen-options/index.md) +- [JSON Output](json.md) - [Targets](targets/index.md) - [Built-in Targets](targets/built-in.md) - [Custom Targets](targets/custom.md) + - [Known Issues](targets/known-issues.md) - [Profile-guided Optimization](profile-guided-optimization.md) - [Linker-plugin based LTO](linker-plugin-lto.md) - [Contributing to `rustc`](contributing.md)
diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 5c41acc..f5d5f20 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md
@@ -61,6 +61,8 @@ To see the valid options and an example of use, run `rustc --print target-features`. +Using this flag is unsafe and might result in [undefined runtime behavior](../targets/known-issues.md). + ## passes This flag can be used to add extra LLVM passes to the compilation. @@ -105,7 +107,7 @@ ## no-vectorize-slp -By default, `rustc` will attempt to vectorize loops using [superword-level +By default, `rustc` will attempt to vectorize code using [superword-level parallelism](https://llvm.org/docs/Vectorizers.html#the-slp-vectorizer). This flag will turn that behavior off.
diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 5eea9c8..bdb3c51 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md
@@ -92,6 +92,7 @@ [edition guide]: ../edition-guide/introduction.html ## `--emit`: specifies the types of output files to generate + <a id="option-emit"></a> This flag controls the types of output files generated by the compiler. It accepts a comma-separated list of values, and may be specified multiple times. @@ -144,7 +145,7 @@ target CPU may be selected with the `-C target-cpu=val` flag. - `target-features` — List of available target features for the current target. Target features may be enabled with the `-C target-feature=val` - flag. + flag. This flag is unsafe. See [known issues](targets/known-issues.md) for more details. - `relocation-models` — List of relocation models. Relocation models may be selected with the `-C relocation-model=val` flag. - `code-models` — List of code models. Code models may be selected with the @@ -241,12 +242,13 @@ distribution; this flag allows that to be overridden. ## `--error-format`: control how errors are produced + <a id="option-error-format"></a> This flag lets you control the format of messages. Messages are printed to stderr. The valid options are: - `human` — Human-readable output. This is the default. -- `json` — Structured JSON output. +- `json` — Structured JSON output. See [the JSON chapter] for more detail. - `short` — Short, one-line messages. ## `--color`: configure coloring of output @@ -273,6 +275,7 @@ `foo/lib.rs` but not `./foo/lib.rs`. ## `--json`: configure json messages printed by the compiler + <a id="option-json"></a> When the `--error-format=json` option is passed to rustc then all of the compiler's diagnostic output will be emitted in the form of JSON blobs. The @@ -305,9 +308,13 @@ Note that it is invalid to combine the `--json` argument with the `--color` argument, and it is required to combine `--json` with `--error-format=json`. +See [the JSON chapter] for more detail. + ## `@path`: load command-line flags from a path If you specify `@path` on the command-line, then it will open `path` and read command line options from it. These options are one per line; a blank line indicates an empty option. The file can use Unix or Windows style line endings, and must be encoded as UTF-8. + +[the JSON chapter]: json.md
diff --git a/src/doc/rustc/src/json.md b/src/doc/rustc/src/json.md new file mode 100644 index 0000000..b737849 --- /dev/null +++ b/src/doc/rustc/src/json.md
@@ -0,0 +1,231 @@ +# JSON Output + +This chapter documents the JSON structures emitted by `rustc`. JSON may be +enabled with the [`--error-format=json` flag][option-error-format]. Additional +options may be specified with the [`--json` flag][option-json] which can +change which messages are generated, and the format of the messages. + +JSON messages are emitted one per line to stderr. + +If parsing the output with Rust, the +[`cargo_metadata`](https://crates.io/crates/cargo_metadata) crate provides +some support for parsing the messages. + +When parsing, care should be taken to be forwards-compatible with future changes +to the format. Optional values may be `null`. New fields may be added. Enumerated +fields like "level" or "suggestion_applicability" may add new values. + +## Diagnostics + +Diagnostic messages provide errors or possible concerns generated during +compilation. `rustc` provides detailed information about where the diagnostic +originates, along with hints and suggestions. + +Diagnostics are arranged in a parent/child relationship where the parent +diagnostic value is the core of the diagnostic, and the attached children +provide additional context, help, and information. + +Diagnostics have the following format: + +```javascript +{ + /* The primary message. */ + "message": "unused variable: `x`", + /* The diagnostic code. + Some messages may set this value to null. + */ + "code": { + /* A unique string identifying which diagnostic triggered. */ + "code": "unused_variables", + /* An optional string explaining more detail about the diagnostic code. */ + "explanation": null + }, + /* The severity of the diagnostic. + Values may be: + - "error": A fatal error that prevents compilation. + - "warning": A possible error or concern. + - "note": Additional information or context about the diagnostic. + - "help": A suggestion on how to resolve the diagnostic. + - "failure-note": A note attached to the message for further information. + - "error: internal compiler error": Indicates a bug within the compiler. + */ + "level": "warning", + /* An array of source code locations to point out specific details about + where the diagnostic originates from. This may be empty, for example + for some global messages, or child messages attached to a parent. + + Character offsets are offsets of Unicode Scalar Values. + */ + "spans": [ + { + /* The file where the span is located. + For spans located within a macro expansion, this will be the + name of the expanded macro in the format "<MACRONAME macros>". + */ + "file_name": "lib.rs", + /* The byte offset where the span starts (0-based, inclusive). */ + "byte_start": 21, + /* The byte offset where the span ends (0-based, exclusive). */ + "byte_end": 22, + /* The first line number of the span (1-based, inclusive). */ + "line_start": 2, + /* The last line number of the span (1-based, inclusive). */ + "line_end": 2, + /* The first character offset of the line_start (1-based, inclusive). */ + "column_start": 9, + /* The last character offset of the line_end (1-based, exclusive). */ + "column_end": 10, + /* Whether or not this is the "primary" span. + + This indicates that this span is the focal point of the + diagnostic. + + There are rare cases where multiple spans may be marked as + primary. For example, "immutable borrow occurs here" and + "mutable borrow ends here" can be two separate primary spans. + + The top (parent) message should always have at least one + primary span, unless it has zero spans. Child messages may have + zero or more primary spans. + */ + "is_primary": true, + /* An array of objects showing the original source code for this + span. This shows the entire lines of text where the span is + located. A span across multiple lines will have a separate + value for each line. + */ + "text": [ + { + /* The entire line of the original source code. */ + "text": " let x = 123;", + /* The first character offset of the line of + where the span covers this line (1-based, inclusive). */ + "highlight_start": 9, + /* The last character offset of the line of + where the span covers this line (1-based, exclusive). */ + "highlight_end": 10 + } + ], + /* An optional message to display at this span location. + This is typically null for primary spans. + */ + "label": null, + /* An optional string of a suggested replacement for this span to + solve the issue. Tools may try to replace the contents of the + span with this text. + */ + "suggested_replacement": null, + /* An optional string that indicates the confidence of the + "suggested_replacement". Tools may use this value to determine + whether or not suggestions should be automatically applied. + + Possible values may be: + - "MachineApplicable": The suggestion is definitely what the + user intended. This suggestion should be automatically + applied. + - "MaybeIncorrect": The suggestion may be what the user + intended, but it is uncertain. The suggestion should result + in valid Rust code if it is applied. + - "HasPlaceholders": The suggestion contains placeholders like + `(...)`. The suggestion cannot be applied automatically + because it will not result in valid Rust code. The user will + need to fill in the placeholders. + - "Unspecified": The applicability of the suggestion is unknown. + */ + "suggestion_applicability": null, + /* An optional object indicating the expansion of a macro within + this span. + + If a message occurs within a macro invocation, this object will + provide details of where within the macro expansion the message + is located. + */ + "expansion": { + /* The span of the macro invocation. + Uses the same span definition as the "spans" array. + */ + "span": {/*...*/} + /* Name of the macro, such as "foo!" or "#[derive(Eq)]". */ + "macro_decl_name": "some_macro!", + /* Optional span where the relevant part of the macro is + defined. */ + "def_site_span": {/*...*/}, + } + } + ], + /* Array of attached diagnostic messages. + This is an array of objects using the same format as the parent + message. Children are not nested (children do not themselves + contain "children" definitions). + */ + "children": [ + { + "message": "`#[warn(unused_variables)]` on by default", + "code": null, + "level": "note", + "spans": [], + "children": [], + "rendered": null + }, + { + "message": "consider prefixing with an underscore", + "code": null, + "level": "help", + "spans": [ + { + "file_name": "lib.rs", + "byte_start": 21, + "byte_end": 22, + "line_start": 2, + "line_end": 2, + "column_start": 9, + "column_end": 10, + "is_primary": true, + "text": [ + { + "text": " let x = 123;", + "highlight_start": 9, + "highlight_end": 10 + } + ], + "label": null, + "suggested_replacement": "_x", + "suggestion_applicability": "MachineApplicable", + "expansion": null + } + ], + "children": [], + "rendered": null + } + ], + /* Optional string of the rendered version of the diagnostic as displayed + by rustc. Note that this may be influenced by the `--json` flag. + */ + "rendered": "warning: unused variable: `x`\n --> lib.rs:2:9\n |\n2 | let x = 123;\n | ^ help: consider prefixing with an underscore: `_x`\n |\n = note: `#[warn(unused_variables)]` on by default\n\n" +} +``` + +## Artifact notifications + +Artifact notifications are emitted when the [`--json=artifacts` +flag][option-json] is used. They indicate that a file artifact has been saved +to disk. More information about emit kinds may be found in the [`--emit` +flag][option-emit] documentation. + +```javascript +{ + /* The filename that was generated. */ + "artifact": "libfoo.rlib", + /* The kind of artifact that was generated. Possible values: + - "link": The generated crate as specified by the crate-type. + - "dep-info": The `.d` file with dependency information in a Makefile-like syntax. + - "metadata": The Rust `.rmeta` file containing metadata about the crate. + - "save-analysis": A JSON file emitted by the `-Zsave-analysis` feature. + */ + "emit": "link" +} +``` + +[option-emit]: command-line-arguments.md#option-emit +[option-error-format]: command-line-arguments.md#option-error-format +[option-json]: command-line-arguments.md#option-json
diff --git a/src/doc/rustc/src/lints/listing/deny-by-default.md b/src/doc/rustc/src/lints/listing/deny-by-default.md index 6574267..5688e90 100644 --- a/src/doc/rustc/src/lints/listing/deny-by-default.md +++ b/src/doc/rustc/src/lints/listing/deny-by-default.md
@@ -222,3 +222,28 @@ | ^^^^^^^^^^^^^^^^^^^^ | ``` + +## const-err + +This lint detects expressions that will always panic at runtime and would be an +error in a `const` context. + +```rust,ignore +let _ = [0; 4][4]; +``` + +This will produce: + +```text +error: index out of bounds: the len is 4 but the index is 4 + --> src/lib.rs:1:9 + | +1 | let _ = [0; 4][4]; + | ^^^^^^^^^ + | +``` + +## order-dependent-trait-objects + +This lint detects a trait coherency violation that would allow creating two +trait impls for the same dynamic trait object involving marker traits.
diff --git a/src/doc/rustc/src/lints/listing/warn-by-default.md b/src/doc/rustc/src/lints/listing/warn-by-default.md index e486240..813d7c4 100644 --- a/src/doc/rustc/src/lints/listing/warn-by-default.md +++ b/src/doc/rustc/src/lints/listing/warn-by-default.md
@@ -596,30 +596,6 @@ | ``` -## unions-with-drop-fields - -This lint detects use of unions that contain fields with possibly non-trivial drop code. Some -example code that triggers this lint: - -```rust -#![feature(untagged_unions)] - -union U { - s: String, -} -``` - -This will produce: - -```text -warning: union contains a field with possibly non-trivial drop code, drop code of union fields is ignored when dropping the union - --> src/main.rs:4:5 - | -4 | s: String, - | ^^^^^^^^^ - | -``` - ## unknown-lints This lint detects unrecognized lint attribute. Some
diff --git a/src/doc/rustc/src/targets/index.md b/src/doc/rustc/src/targets/index.md index 3d63d07..5859df8 100644 --- a/src/doc/rustc/src/targets/index.md +++ b/src/doc/rustc/src/targets/index.md
@@ -11,3 +11,9 @@ ```bash $ rustc src/main.rs --target=wasm32-unknown-unknown ``` +## Target Features +`x86`, and `ARMv8` are two popular CPU architectures. Their instruction sets form a common baseline across most CPUs. However, some CPUs extend these with custom instruction sets, e.g. vector (`AVX`), bitwise manipulation (`BMI`) or cryptographic (`AES`). + +Developers, who know on which CPUs their compiled code is going to run can choose to add (or remove) CPU specific instruction sets via the `-C target-feature=val` flag. + +Please note, that this flag is generally considered as unsafe. More details can be found in [this section](known-issues.md).
diff --git a/src/doc/rustc/src/targets/known-issues.md b/src/doc/rustc/src/targets/known-issues.md new file mode 100644 index 0000000..89fd8ea --- /dev/null +++ b/src/doc/rustc/src/targets/known-issues.md
@@ -0,0 +1,13 @@ +# Known Issues +This section informs you about known "gotchas". Keep in mind, that this section is (and always will be) incomplete. For suggestions and amendments, feel free to [contribute](../contributing.md) to this guide. + +## Target Features +Most target-feature problems arise, when mixing code that have the target-feature _enabled_ with code that have it _disabled_. If you want to avoid undefined behavior, it is recommended to build _all code_ (including the standard library and imported crates) with a common set of target-features. + +By default, compiling your code with the `-C target-feature` flag will not recompile the entire standard library and/or imported crates with matching target features. Therefore, target features are generally considered as unsafe. Using `#[target_feature]` on individual functions makes the function unsafe. + +Examples: + +| Target-Feature | Issue | Seen on | Description | Details | +| -------------- | ----- | ------- | ----------- | ------- | +| `+soft-float` <br> and <br> `-sse` | Segfaults and ABI mismatches | `x86` and `x86-64` | The `x86` and `x86_64` architecture uses SSE registers (aka `xmm`) for floating point operations. Using software emulated floats ("soft-floats") disables usage of `xmm` registers, but parts of Rust's core libraries (e.g. `std::f32` or `std::f64`) are compiled without soft-floats and expect parameters to be passed in `xmm` registers. This leads to ABI mismatches. <br><br> Attempting to compile with disabled SSE causes the same error, too. | [#63466](https://github.com/rust-lang/rust/issues/63466) |
diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index c9acd3c..bc1da5f 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/documentation-tests.md
@@ -379,3 +379,49 @@ Not only are fenced code blocks considered more idiomatic for Rust code, but there is no way to use directives such as `ignore` or `should_panic` with indented code blocks. + +### Include items only when collecting doctests + +Rustdoc's documentation tests can do some things that regular unit tests can't, so it can +sometimes be useful to extend your doctests with samples that wouldn't otherwise need to be in +documentation. To this end, Rustdoc allows you to have certain items only appear when it's +collecting doctests, so you can utilize doctest functionality without forcing the test to appear in +docs, or to find an arbitrary private item to include it on. + +When compiling a crate for use in doctests (with `--test` option), rustdoc will set `cfg(doctest)`. +Note that they will still link against only the public items of your crate; if you need to test +private items, you need to write a unit test. + +In this example, we're adding doctests that we know won't compile, to verify that our struct can +only take in valid data: + +```rust +/// We have a struct here. Remember it doesn't accept negative numbers! +pub struct MyStruct(pub usize); + +/// ```compile_fail +/// let x = my_crate::MyStruct(-5); +/// ``` +#[cfg(doctest)] +pub struct MyStructOnlyTakesUsize; +``` + +Note that the struct `MyStructOnlyTakesUsize` here isn't actually part of your public crate +API. The use of `#[cfg(doctest)]` makes sure that this struct only exists while rustdoc is +collecting doctests. This means that its doctest is executed when `--test` is passed to rustdoc, +but is hidden from the public documentation. + +Another possible use of `cfg(doctest)` is to test doctests that are included in your README file +without including it in your main documentation. For example, you could write this into your +`lib.rs` to test your README as part of your doctests: + +```rust,ignore +#![feature(extern_doc)] + +#[doc(include="../README.md")] +#[cfg(doctest)] +pub struct ReadmeDoctests; +``` + +This will include your README as documentation on the hidden struct `ReadmeDoctests`, which will +then be tested alongside the rest of your doctests.
diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 49d05b5..3c3e72a 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md
@@ -211,36 +211,6 @@ Then, when looking for it through the `rustdoc` search, if you enter "x" or "big", search will show the `BigX` struct first. -### Include items only when collecting doctests - -Rustdoc's [documentation tests] can do some things that regular unit tests can't, so it can -sometimes be useful to extend your doctests with samples that wouldn't otherwise need to be in -documentation. To this end, Rustdoc allows you to have certain items only appear when it's -collecting doctests, so you can utilize doctest functionality without forcing the test to appear in -docs, or to find an arbitrary private item to include it on. - -If you add `#![feature(cfg_doctest)]` to your crate, Rustdoc will set `cfg(doctest)` when collecting -doctests. Note that they will still link against only the public items of your crate; if you need to -test private items, unit tests are still the way to go. - -In this example, we're adding doctests that we know won't compile, to verify that our struct can -only take in valid data: - -```rust -#![feature(cfg_doctest)] - -/// We have a struct here. Remember it doesn't accept negative numbers! -pub struct MyStruct(usize); - -/// ```compile_fail -/// let x = my_crate::MyStruct(-5); -/// ``` -#[cfg(doctest)] -pub struct MyStructOnlyTakesUsize; -``` - -[documentation tests]: documentation-tests.html - ## Unstable command-line arguments These features are enabled by passing a command-line flag to Rustdoc, but the flags in question are
diff --git a/src/doc/unstable-book/src/compiler-flags/report-time.md b/src/doc/unstable-book/src/compiler-flags/report-time.md new file mode 100644 index 0000000..ed4e9c6 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/report-time.md
@@ -0,0 +1,80 @@ +# `report-time` + +The tracking issue for this feature is: [#64888] + +[#64888]: https://github.com/rust-lang/rust/issues/64888 + +------------------------ + +The `report-time` feature adds a possibility to report execution time of the +tests generated via `libtest`. + +This is unstable feature, so you have to provide `-Zunstable-options` to get +this feature working. + +Sample usage command: + +```sh +./test_executable -Zunstable-options --report-time +``` + +Available options: + +```sh +--report-time [plain|colored] + Show execution time of each test. Awailable values: + plain = do not colorize the execution time (default); + colored = colorize output according to the `color` + parameter value; + Threshold values for colorized output can be + configured via + `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` + and + `RUST_TEST_TIME_DOCTEST` environment variables. + Expected format of environment variable is + `VARIABLE=WARN_TIME,CRITICAL_TIME`. + Not available for --format=terse +--ensure-time + Treat excess of the test execution time limit as + error. + Threshold values for this option can be configured via + `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` + and + `RUST_TEST_TIME_DOCTEST` environment variables. + Expected format of environment variable is + `VARIABLE=WARN_TIME,CRITICAL_TIME`. + `CRITICAL_TIME` here means the limit that should not be + exceeded by test. +``` + +Example of the environment variable format: + +```sh +RUST_TEST_TIME_UNIT=100,200 +``` + +where 100 stands for warn time, and 200 stands for critical time. + +## Examples + +```sh +cargo test --tests -- -Zunstable-options --report-time + Finished dev [unoptimized + debuginfo] target(s) in 0.02s + Running target/debug/deps/example-27fb188025bec02c + +running 3 tests +test tests::unit_test_quick ... ok <0.000s> +test tests::unit_test_warn ... ok <0.055s> +test tests::unit_test_critical ... ok <0.110s> + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out + + Running target/debug/deps/tests-cedb06f6526d15d9 + +running 3 tests +test unit_test_quick ... ok <0.000s> +test unit_test_warn ... ok <0.550s> +test unit_test_critical ... ok <1.100s> + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +```
diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index 3ee024c..d4ad65e 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md
@@ -249,11 +249,11 @@ - Runtime - `start`: `libstd/rt.rs` - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC) - - `eh_personality`: `libpanic_unwind/seh64_gnu.rs` (SEH64 GNU) + - `eh_personality`: `libpanic_unwind/gcc.rs` (GNU) - `eh_personality`: `libpanic_unwind/seh.rs` (SEH) - - `eh_unwind_resume`: `libpanic_unwind/seh64_gnu.rs` (SEH64 GNU) - `eh_unwind_resume`: `libpanic_unwind/gcc.rs` (GCC) - - `msvc_try_filter`: `libpanic_unwind/seh.rs` (SEH) + - `eh_catch_typeinfo`: `libpanic_unwind/seh.rs` (SEH) + - `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC) - `panic`: `libcore/panicking.rs` - `panic_bounds_check`: `libcore/panicking.rs` - `panic_impl`: `libcore/panicking.rs`
diff --git a/src/doc/unstable-book/src/language-features/non-exhaustive.md b/src/doc/unstable-book/src/language-features/non-exhaustive.md deleted file mode 100644 index 907147c..0000000 --- a/src/doc/unstable-book/src/language-features/non-exhaustive.md +++ /dev/null
@@ -1,76 +0,0 @@ -# `non_exhaustive` - -The tracking issue for this feature is: [#44109] - -[#44109]: https://github.com/rust-lang/rust/issues/44109 - ------------------------- - -The `non_exhaustive` gate allows you to use the `#[non_exhaustive]` attribute -on structs, enums and enum variants. When applied within a crate, users of the -crate will need to use the `_` pattern when matching enums and use the `..` -pattern when matching structs. Enum variants cannot be matched against. -Structs and enum variants marked as `non_exhaustive` will not be able to -be created normally outside of the defining crate. This is demonstrated -below: - -```rust,ignore (pseudo-Rust) -use std::error::Error as StdError; - -#[non_exhaustive] -pub enum Error { - Message(String), - Other, -} -impl StdError for Error { - fn description(&self) -> &str { - // This will not error, despite being marked as non_exhaustive, as this - // enum is defined within the current crate, it can be matched - // exhaustively. - match *self { - Message(ref s) => s, - Other => "other or unknown error", - } - } -} -``` - -```rust,ignore (pseudo-Rust) -use mycrate::Error; - -// This will not error as the non_exhaustive Error enum has been matched with -// a wildcard. -match error { - Message(ref s) => ..., - Other => ..., - _ => ..., -} -``` - -```rust,ignore (pseudo-Rust) -#[non_exhaustive] -pub struct Config { - pub window_width: u16, - pub window_height: u16, -} - -// We can create structs as normal within the defining crate when marked as -// non_exhaustive. -let config = Config { window_width: 640, window_height: 480 }; - -// We can match structs exhaustively when within the defining crate. -if let Ok(Config { window_width, window_height }) = load_config() { - // ... -} -``` - -```rust,ignore (pseudo-Rust) -use mycrate::Config; - -// We cannot create a struct like normal if it has been marked as -// non_exhaustive. -let config = Config { window_width: 640, window_height: 480 }; -// By adding the `..` we can match the config as below outside of the crate -// when marked non_exhaustive. -let &Config { window_width, window_height, .. } = config; -```
diff --git a/src/doc/unstable-book/src/language-features/track-caller.md b/src/doc/unstable-book/src/language-features/track-caller.md new file mode 100644 index 0000000..afc11a2 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/track-caller.md
@@ -0,0 +1,5 @@ +# `track_caller` + +The tracking issue for this feature is: [#47809](https://github.com/rust-lang/rust/issues/47809). + +------------------------
diff --git a/src/grammar/lexer.l b/src/grammar/lexer.l deleted file mode 100644 index 1feb781..0000000 --- a/src/grammar/lexer.l +++ /dev/null
@@ -1,350 +0,0 @@ -%{ -#include <stdio.h> -#include <ctype.h> - -static int num_hashes; -static int end_hashes; -static int saw_non_hash; - -%} - -%option stack -%option yylineno - -%x str -%x rawstr -%x rawstr_esc_begin -%x rawstr_esc_body -%x rawstr_esc_end -%x byte -%x bytestr -%x rawbytestr -%x rawbytestr_nohash -%x pound -%x shebang_or_attr -%x ltorchar -%x linecomment -%x doc_line -%x blockcomment -%x doc_block -%x suffix - -ident [a-zA-Z\x80-\xff_][a-zA-Z0-9\x80-\xff_]* - -%% - -<suffix>{ident} { BEGIN(INITIAL); } -<suffix>(.|\n) { yyless(0); BEGIN(INITIAL); } - -[ \n\t\r] { } - -\xef\xbb\xbf { - // UTF-8 byte order mark (BOM), ignore if in line 1, error otherwise - if (yyget_lineno() != 1) { - return -1; - } -} - -\/\/(\/|\!) { BEGIN(doc_line); yymore(); } -<doc_line>\n { BEGIN(INITIAL); - yyleng--; - yytext[yyleng] = 0; - return ((yytext[2] == '!') ? INNER_DOC_COMMENT : OUTER_DOC_COMMENT); - } -<doc_line>[^\n]* { yymore(); } - -\/\/|\/\/\/\/ { BEGIN(linecomment); } -<linecomment>\n { BEGIN(INITIAL); } -<linecomment>[^\n]* { } - -\/\*(\*|\!)[^*] { yy_push_state(INITIAL); yy_push_state(doc_block); yymore(); } -<doc_block>\/\* { yy_push_state(doc_block); yymore(); } -<doc_block>\*\/ { - yy_pop_state(); - if (yy_top_state() == doc_block) { - yymore(); - } else { - return ((yytext[2] == '!') ? INNER_DOC_COMMENT : OUTER_DOC_COMMENT); - } -} -<doc_block>(.|\n) { yymore(); } - -\/\* { yy_push_state(blockcomment); } -<blockcomment>\/\* { yy_push_state(blockcomment); } -<blockcomment>\*\/ { yy_pop_state(); } -<blockcomment>(.|\n) { } - -_ { return UNDERSCORE; } -abstract { return ABSTRACT; } -alignof { return ALIGNOF; } -as { return AS; } -become { return BECOME; } -box { return BOX; } -break { return BREAK; } -catch { return CATCH; } -const { return CONST; } -continue { return CONTINUE; } -crate { return CRATE; } -default { return DEFAULT; } -do { return DO; } -else { return ELSE; } -enum { return ENUM; } -extern { return EXTERN; } -false { return FALSE; } -final { return FINAL; } -fn { return FN; } -for { return FOR; } -if { return IF; } -impl { return IMPL; } -in { return IN; } -let { return LET; } -loop { return LOOP; } -macro { return MACRO; } -match { return MATCH; } -mod { return MOD; } -move { return MOVE; } -mut { return MUT; } -offsetof { return OFFSETOF; } -override { return OVERRIDE; } -priv { return PRIV; } -proc { return PROC; } -pure { return PURE; } -pub { return PUB; } -ref { return REF; } -return { return RETURN; } -self { return SELF; } -sizeof { return SIZEOF; } -static { return STATIC; } -struct { return STRUCT; } -super { return SUPER; } -trait { return TRAIT; } -true { return TRUE; } -type { return TYPE; } -typeof { return TYPEOF; } -union { return UNION; } -unsafe { return UNSAFE; } -unsized { return UNSIZED; } -use { return USE; } -virtual { return VIRTUAL; } -where { return WHERE; } -while { return WHILE; } -yield { return YIELD; } - -{ident} { return IDENT; } - -0x[0-9a-fA-F_]+ { BEGIN(suffix); return LIT_INTEGER; } -0o[0-7_]+ { BEGIN(suffix); return LIT_INTEGER; } -0b[01_]+ { BEGIN(suffix); return LIT_INTEGER; } -[0-9][0-9_]* { BEGIN(suffix); return LIT_INTEGER; } -[0-9][0-9_]*\.(\.|[a-zA-Z]) { yyless(yyleng - 2); BEGIN(suffix); return LIT_INTEGER; } - -[0-9][0-9_]*\.[0-9_]*([eE][-\+]?[0-9_]+)? { BEGIN(suffix); return LIT_FLOAT; } -[0-9][0-9_]*(\.[0-9_]*)?[eE][-\+]?[0-9_]+ { BEGIN(suffix); return LIT_FLOAT; } - -; { return ';'; } -, { return ','; } -\.\.\. { return DOTDOTDOT; } -\.\. { return DOTDOT; } -\. { return '.'; } -\( { return '('; } -\) { return ')'; } -\{ { return '{'; } -\} { return '}'; } -\[ { return '['; } -\] { return ']'; } -@ { return '@'; } -# { BEGIN(pound); yymore(); } -<pound>\! { BEGIN(shebang_or_attr); yymore(); } -<shebang_or_attr>\[ { - BEGIN(INITIAL); - yyless(2); - return SHEBANG; -} -<shebang_or_attr>[^\[\n]*\n { - // Since the \n was eaten as part of the token, yylineno will have - // been incremented to the value 2 if the shebang was on the first - // line. This yyless undoes that, setting yylineno back to 1. - yyless(yyleng - 1); - if (yyget_lineno() == 1) { - BEGIN(INITIAL); - return SHEBANG_LINE; - } else { - BEGIN(INITIAL); - yyless(2); - return SHEBANG; - } -} -<pound>. { BEGIN(INITIAL); yyless(1); return '#'; } - -\~ { return '~'; } -:: { return MOD_SEP; } -: { return ':'; } -\$ { return '$'; } -\? { return '?'; } - -== { return EQEQ; } -=> { return FAT_ARROW; } -= { return '='; } -\!= { return NE; } -\! { return '!'; } -\<= { return LE; } -\<\< { return SHL; } -\<\<= { return SHLEQ; } -\< { return '<'; } -\>= { return GE; } -\>\> { return SHR; } -\>\>= { return SHREQ; } -\> { return '>'; } - -\x27 { BEGIN(ltorchar); yymore(); } -<ltorchar>static { BEGIN(INITIAL); return STATIC_LIFETIME; } -<ltorchar>{ident} { BEGIN(INITIAL); return LIFETIME; } -<ltorchar>\\[nrt\\\x27\x220]\x27 { BEGIN(suffix); return LIT_CHAR; } -<ltorchar>\\x[0-9a-fA-F]{2}\x27 { BEGIN(suffix); return LIT_CHAR; } -<ltorchar>\\u\{([0-9a-fA-F]_*){1,6}\}\x27 { BEGIN(suffix); return LIT_CHAR; } -<ltorchar>.\x27 { BEGIN(suffix); return LIT_CHAR; } -<ltorchar>[\x80-\xff]{2,4}\x27 { BEGIN(suffix); return LIT_CHAR; } -<ltorchar><<EOF>> { BEGIN(INITIAL); return -1; } - -b\x22 { BEGIN(bytestr); yymore(); } -<bytestr>\x22 { BEGIN(suffix); return LIT_BYTE_STR; } - -<bytestr><<EOF>> { return -1; } -<bytestr>\\[n\nrt\\\x27\x220] { yymore(); } -<bytestr>\\x[0-9a-fA-F]{2} { yymore(); } -<bytestr>\\u\{([0-9a-fA-F]_*){1,6}\} { yymore(); } -<bytestr>\\[^n\nrt\\\x27\x220] { return -1; } -<bytestr>(.|\n) { yymore(); } - -br\x22 { BEGIN(rawbytestr_nohash); yymore(); } -<rawbytestr_nohash>\x22 { BEGIN(suffix); return LIT_BYTE_STR_RAW; } -<rawbytestr_nohash>(.|\n) { yymore(); } -<rawbytestr_nohash><<EOF>> { return -1; } - -br/# { - BEGIN(rawbytestr); - yymore(); - num_hashes = 0; - saw_non_hash = 0; - end_hashes = 0; -} -<rawbytestr># { - if (!saw_non_hash) { - num_hashes++; - } else if (end_hashes != 0) { - end_hashes++; - if (end_hashes == num_hashes) { - BEGIN(INITIAL); - return LIT_BYTE_STR_RAW; - } - } - yymore(); -} -<rawbytestr>\x22# { - end_hashes = 1; - if (end_hashes == num_hashes) { - BEGIN(INITIAL); - return LIT_BYTE_STR_RAW; - } - yymore(); -} -<rawbytestr>(.|\n) { - if (!saw_non_hash) { - saw_non_hash = 1; - } - if (end_hashes != 0) { - end_hashes = 0; - } - yymore(); -} -<rawbytestr><<EOF>> { return -1; } - -b\x27 { BEGIN(byte); yymore(); } -<byte>\\[nrt\\\x27\x220]\x27 { BEGIN(INITIAL); return LIT_BYTE; } -<byte>\\x[0-9a-fA-F]{2}\x27 { BEGIN(INITIAL); return LIT_BYTE; } -<byte>\\u([0-9a-fA-F]_*){4}\x27 { BEGIN(INITIAL); return LIT_BYTE; } -<byte>\\U([0-9a-fA-F]_*){8}\x27 { BEGIN(INITIAL); return LIT_BYTE; } -<byte>.\x27 { BEGIN(INITIAL); return LIT_BYTE; } -<byte><<EOF>> { BEGIN(INITIAL); return -1; } - -r\x22 { BEGIN(rawstr); yymore(); } -<rawstr>\x22 { BEGIN(suffix); return LIT_STR_RAW; } -<rawstr>(.|\n) { yymore(); } -<rawstr><<EOF>> { return -1; } - -r/# { - BEGIN(rawstr_esc_begin); - yymore(); - num_hashes = 0; - saw_non_hash = 0; - end_hashes = 0; -} - -<rawstr_esc_begin># { - num_hashes++; - yymore(); -} -<rawstr_esc_begin>\x22 { - BEGIN(rawstr_esc_body); - yymore(); -} -<rawstr_esc_begin>(.|\n) { return -1; } - -<rawstr_esc_body>\x22/# { - BEGIN(rawstr_esc_end); - yymore(); - } -<rawstr_esc_body>(.|\n) { - yymore(); - } - -<rawstr_esc_end># { - end_hashes++; - if (end_hashes == num_hashes) { - BEGIN(INITIAL); - return LIT_STR_RAW; - } - yymore(); - } -<rawstr_esc_end>[^#] { - end_hashes = 0; - BEGIN(rawstr_esc_body); - yymore(); - } - -<rawstr_esc_begin,rawstr_esc_body,rawstr_esc_end><<EOF>> { return -1; } - -\x22 { BEGIN(str); yymore(); } -<str>\x22 { BEGIN(suffix); return LIT_STR; } - -<str><<EOF>> { return -1; } -<str>\\[n\nr\rt\\\x27\x220] { yymore(); } -<str>\\x[0-9a-fA-F]{2} { yymore(); } -<str>\\u\{([0-9a-fA-F]_*){1,6}\} { yymore(); } -<str>\\[^n\nrt\\\x27\x220] { return -1; } -<str>(.|\n) { yymore(); } - -\<- { return LARROW; } --\> { return RARROW; } -- { return '-'; } --= { return MINUSEQ; } -&& { return ANDAND; } -& { return '&'; } -&= { return ANDEQ; } -\|\| { return OROR; } -\| { return '|'; } -\|= { return OREQ; } -\+ { return '+'; } -\+= { return PLUSEQ; } -\* { return '*'; } -\*= { return STAREQ; } -\/ { return '/'; } -\/= { return SLASHEQ; } -\^ { return '^'; } -\^= { return CARETEQ; } -% { return '%'; } -%= { return PERCENTEQ; } - -<<EOF>> { return 0; } - -%%
diff --git a/src/grammar/parser-lalr-main.c b/src/grammar/parser-lalr-main.c deleted file mode 100644 index 6348190..0000000 --- a/src/grammar/parser-lalr-main.c +++ /dev/null
@@ -1,193 +0,0 @@ -#include <stdio.h> -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> - -extern int yylex(); -extern int rsparse(); - -#define PUSHBACK_LEN 4 - -static char pushback[PUSHBACK_LEN]; -static int verbose; - -void print(const char* format, ...) { - va_list args; - va_start(args, format); - if (verbose) { - vprintf(format, args); - } - va_end(args); -} - -// If there is a non-null char at the head of the pushback queue, -// dequeue it and shift the rest of the queue forwards. Otherwise, -// return the token from calling yylex. -int rslex() { - if (pushback[0] == '\0') { - return yylex(); - } else { - char c = pushback[0]; - memmove(pushback, pushback + 1, PUSHBACK_LEN - 1); - pushback[PUSHBACK_LEN - 1] = '\0'; - return c; - } -} - -// Note: this does nothing if the pushback queue is full. As long as -// there aren't more than PUSHBACK_LEN consecutive calls to push_back -// in an action, this shouldn't be a problem. -void push_back(char c) { - for (int i = 0; i < PUSHBACK_LEN; ++i) { - if (pushback[i] == '\0') { - pushback[i] = c; - break; - } - } -} - -extern int rsdebug; - -struct node { - struct node *next; - struct node *prev; - int own_string; - char const *name; - int n_elems; - struct node *elems[]; -}; - -struct node *nodes = NULL; -int n_nodes; - -struct node *mk_node(char const *name, int n, ...) { - va_list ap; - int i = 0; - unsigned sz = sizeof(struct node) + (n * sizeof(struct node *)); - struct node *nn, *nd = (struct node *)malloc(sz); - - print("# New %d-ary node: %s = %p\n", n, name, nd); - - nd->own_string = 0; - nd->prev = NULL; - nd->next = nodes; - if (nodes) { - nodes->prev = nd; - } - nodes = nd; - - nd->name = name; - nd->n_elems = n; - - va_start(ap, n); - while (i < n) { - nn = va_arg(ap, struct node *); - print("# arg[%d]: %p\n", i, nn); - print("# (%s ...)\n", nn->name); - nd->elems[i++] = nn; - } - va_end(ap); - n_nodes++; - return nd; -} - -struct node *mk_atom(char *name) { - struct node *nd = mk_node((char const *)strdup(name), 0); - nd->own_string = 1; - return nd; -} - -struct node *mk_none() { - return mk_atom("<none>"); -} - -struct node *ext_node(struct node *nd, int n, ...) { - va_list ap; - int i = 0, c = nd->n_elems + n; - unsigned sz = sizeof(struct node) + (c * sizeof(struct node *)); - struct node *nn; - - print("# Extending %d-ary node by %d nodes: %s = %p", - nd->n_elems, c, nd->name, nd); - - if (nd->next) { - nd->next->prev = nd->prev; - } - if (nd->prev) { - nd->prev->next = nd->next; - } - nd = realloc(nd, sz); - nd->prev = NULL; - nd->next = nodes; - nodes->prev = nd; - nodes = nd; - - print(" ==> %p\n", nd); - - va_start(ap, n); - while (i < n) { - nn = va_arg(ap, struct node *); - print("# arg[%d]: %p\n", i, nn); - print("# (%s ...)\n", nn->name); - nd->elems[nd->n_elems++] = nn; - ++i; - } - va_end(ap); - return nd; -} - -int const indent_step = 4; - -void print_indent(int depth) { - while (depth) { - if (depth-- % indent_step == 0) { - print("|"); - } else { - print(" "); - } - } -} - -void print_node(struct node *n, int depth) { - int i = 0; - print_indent(depth); - if (n->n_elems == 0) { - print("%s\n", n->name); - } else { - print("(%s\n", n->name); - for (i = 0; i < n->n_elems; ++i) { - print_node(n->elems[i], depth + indent_step); - } - print_indent(depth); - print(")\n"); - } -} - -int main(int argc, char **argv) { - if (argc == 2 && strcmp(argv[1], "-v") == 0) { - verbose = 1; - } else { - verbose = 0; - } - int ret = 0; - struct node *tmp; - memset(pushback, '\0', PUSHBACK_LEN); - ret = rsparse(); - print("--- PARSE COMPLETE: ret:%d, n_nodes:%d ---\n", ret, n_nodes); - if (nodes) { - print_node(nodes, 0); - } - while (nodes) { - tmp = nodes; - nodes = tmp->next; - if (tmp->own_string) { - free((void*)tmp->name); - } - free(tmp); - } - return ret; -} - -void rserror(char const *s) { - fprintf(stderr, "%s\n", s); -}
diff --git a/src/grammar/parser-lalr.y b/src/grammar/parser-lalr.y deleted file mode 100644 index 5585c95..0000000 --- a/src/grammar/parser-lalr.y +++ /dev/null
@@ -1,1982 +0,0 @@ -%{ -#define YYERROR_VERBOSE -#define YYSTYPE struct node * -struct node; -extern int yylex(); -extern void yyerror(char const *s); -extern struct node *mk_node(char const *name, int n, ...); -extern struct node *mk_atom(char *text); -extern struct node *mk_none(); -extern struct node *ext_node(struct node *nd, int n, ...); -extern void push_back(char c); -extern char *yytext; -%} -%debug - -%token SHL -%token SHR -%token LE -%token EQEQ -%token NE -%token GE -%token ANDAND -%token OROR -%token SHLEQ -%token SHREQ -%token MINUSEQ -%token ANDEQ -%token OREQ -%token PLUSEQ -%token STAREQ -%token SLASHEQ -%token CARETEQ -%token PERCENTEQ -%token DOTDOT -%token DOTDOTDOT -%token MOD_SEP -%token RARROW -%token LARROW -%token FAT_ARROW -%token LIT_BYTE -%token LIT_CHAR -%token LIT_INTEGER -%token LIT_FLOAT -%token LIT_STR -%token LIT_STR_RAW -%token LIT_BYTE_STR -%token LIT_BYTE_STR_RAW -%token IDENT -%token UNDERSCORE -%token LIFETIME - -// keywords -%token SELF -%token STATIC -%token ABSTRACT -%token ALIGNOF -%token AS -%token BECOME -%token BREAK -%token CATCH -%token CRATE -%token DO -%token ELSE -%token ENUM -%token EXTERN -%token FALSE -%token FINAL -%token FN -%token FOR -%token IF -%token IMPL -%token IN -%token LET -%token LOOP -%token MACRO -%token MATCH -%token MOD -%token MOVE -%token MUT -%token OFFSETOF -%token OVERRIDE -%token PRIV -%token PUB -%token PURE -%token REF -%token RETURN -%token SIZEOF -%token STRUCT -%token SUPER -%token UNION -%token UNSIZED -%token TRUE -%token TRAIT -%token TYPE -%token UNSAFE -%token VIRTUAL -%token YIELD -%token DEFAULT -%token USE -%token WHILE -%token CONTINUE -%token PROC -%token BOX -%token CONST -%token WHERE -%token TYPEOF -%token INNER_DOC_COMMENT -%token OUTER_DOC_COMMENT - -%token SHEBANG -%token SHEBANG_LINE -%token STATIC_LIFETIME - - /* - Quoting from the Bison manual: - - "Finally, the resolution of conflicts works by comparing the precedence - of the rule being considered with that of the lookahead token. If the - token's precedence is higher, the choice is to shift. If the rule's - precedence is higher, the choice is to reduce. If they have equal - precedence, the choice is made based on the associativity of that - precedence level. The verbose output file made by ‘-v’ (see Invoking - Bison) says how each conflict was resolved" - */ - -// We expect no shift/reduce or reduce/reduce conflicts in this grammar; -// all potential ambiguities are scrutinized and eliminated manually. -%expect 0 - -// fake-precedence symbol to cause '|' bars in lambda context to parse -// at low precedence, permit things like |x| foo = bar, where '=' is -// otherwise lower-precedence than '|'. Also used for proc() to cause -// things like proc() a + b to parse as proc() { a + b }. -%precedence LAMBDA - -%precedence SELF - -// MUT should be lower precedence than IDENT so that in the pat rule, -// "& MUT pat" has higher precedence than "binding_mode ident [@ pat]" -%precedence MUT - -// IDENT needs to be lower than '{' so that 'foo {' is shifted when -// trying to decide if we've got a struct-construction expr (esp. in -// contexts like 'if foo { .') -// -// IDENT also needs to be lower precedence than '<' so that '<' in -// 'foo:bar . <' is shifted (in a trait reference occurring in a -// bounds list), parsing as foo:(bar<baz>) rather than (foo:bar)<baz>. -%precedence IDENT - // Put the weak keywords that can be used as idents here as well -%precedence CATCH -%precedence DEFAULT -%precedence UNION - -// A couple fake-precedence symbols to use in rules associated with + -// and < in trailing type contexts. These come up when you have a type -// in the RHS of operator-AS, such as "foo as bar<baz>". The "<" there -// has to be shifted so the parser keeps trying to parse a type, even -// though it might well consider reducing the type "bar" and then -// going on to "<" as a subsequent binop. The "+" case is with -// trailing type-bounds ("foo as bar:A+B"), for the same reason. -%precedence SHIFTPLUS - -%precedence MOD_SEP -%precedence RARROW ':' - -// In where clauses, "for" should have greater precedence when used as -// a higher ranked constraint than when used as the beginning of a -// for_in_type (which is a ty) -%precedence FORTYPE -%precedence FOR - -// Binops & unops, and their precedences -%precedence '?' -%precedence BOX -%nonassoc DOTDOT - -// RETURN needs to be lower-precedence than tokens that start -// prefix_exprs -%precedence RETURN YIELD - -%right '=' SHLEQ SHREQ MINUSEQ ANDEQ OREQ PLUSEQ STAREQ SLASHEQ CARETEQ PERCENTEQ -%right LARROW -%left OROR -%left ANDAND -%left EQEQ NE -%left '<' '>' LE GE -%left '|' -%left '^' -%left '&' -%left SHL SHR -%left '+' '-' -%precedence AS -%left '*' '/' '%' -%precedence '!' - -%precedence '{' '[' '(' '.' - -%precedence RANGE - -%start crate - -%% - -//////////////////////////////////////////////////////////////////////// -// Part 1: Items and attributes -//////////////////////////////////////////////////////////////////////// - -crate -: maybe_shebang inner_attrs maybe_mod_items { mk_node("crate", 2, $2, $3); } -| maybe_shebang maybe_mod_items { mk_node("crate", 1, $2); } -; - -maybe_shebang -: SHEBANG_LINE -| %empty -; - -maybe_inner_attrs -: inner_attrs -| %empty { $$ = mk_none(); } -; - -inner_attrs -: inner_attr { $$ = mk_node("InnerAttrs", 1, $1); } -| inner_attrs inner_attr { $$ = ext_node($1, 1, $2); } -; - -inner_attr -: SHEBANG '[' meta_item ']' { $$ = mk_node("InnerAttr", 1, $3); } -| INNER_DOC_COMMENT { $$ = mk_node("InnerAttr", 1, mk_node("doc-comment", 1, mk_atom(yytext))); } -; - -maybe_outer_attrs -: outer_attrs -| %empty { $$ = mk_none(); } -; - -outer_attrs -: outer_attr { $$ = mk_node("OuterAttrs", 1, $1); } -| outer_attrs outer_attr { $$ = ext_node($1, 1, $2); } -; - -outer_attr -: '#' '[' meta_item ']' { $$ = $3; } -| OUTER_DOC_COMMENT { $$ = mk_node("doc-comment", 1, mk_atom(yytext)); } -; - -meta_item -: ident { $$ = mk_node("MetaWord", 1, $1); } -| ident '=' lit { $$ = mk_node("MetaNameValue", 2, $1, $3); } -| ident '(' meta_seq ')' { $$ = mk_node("MetaList", 2, $1, $3); } -| ident '(' meta_seq ',' ')' { $$ = mk_node("MetaList", 2, $1, $3); } -; - -meta_seq -: %empty { $$ = mk_none(); } -| meta_item { $$ = mk_node("MetaItems", 1, $1); } -| meta_seq ',' meta_item { $$ = ext_node($1, 1, $3); } -; - -maybe_mod_items -: mod_items -| %empty { $$ = mk_none(); } -; - -mod_items -: mod_item { $$ = mk_node("Items", 1, $1); } -| mod_items mod_item { $$ = ext_node($1, 1, $2); } -; - -attrs_and_vis -: maybe_outer_attrs visibility { $$ = mk_node("AttrsAndVis", 2, $1, $2); } -; - -mod_item -: attrs_and_vis item { $$ = mk_node("Item", 2, $1, $2); } -; - -// items that can appear outside of a fn block -item -: stmt_item -| item_macro -; - -// items that can appear in "stmts" -stmt_item -: item_static -| item_const -| item_type -| block_item -| view_item -; - -item_static -: STATIC ident ':' ty '=' expr ';' { $$ = mk_node("ItemStatic", 3, $2, $4, $6); } -| STATIC MUT ident ':' ty '=' expr ';' { $$ = mk_node("ItemStatic", 3, $3, $5, $7); } -; - -item_const -: CONST ident ':' ty '=' expr ';' { $$ = mk_node("ItemConst", 3, $2, $4, $6); } -; - -item_macro -: path_expr '!' maybe_ident parens_delimited_token_trees ';' { $$ = mk_node("ItemMacro", 3, $1, $3, $4); } -| path_expr '!' maybe_ident braces_delimited_token_trees { $$ = mk_node("ItemMacro", 3, $1, $3, $4); } -| path_expr '!' maybe_ident brackets_delimited_token_trees ';'{ $$ = mk_node("ItemMacro", 3, $1, $3, $4); } -; - -view_item -: use_item -| extern_fn_item -| EXTERN CRATE ident ';' { $$ = mk_node("ViewItemExternCrate", 1, $3); } -| EXTERN CRATE ident AS ident ';' { $$ = mk_node("ViewItemExternCrate", 2, $3, $5); } -; - -extern_fn_item -: EXTERN maybe_abi item_fn { $$ = mk_node("ViewItemExternFn", 2, $2, $3); } -; - -use_item -: USE view_path ';' { $$ = mk_node("ViewItemUse", 1, $2); } -; - -view_path -: path_no_types_allowed { $$ = mk_node("ViewPathSimple", 1, $1); } -| path_no_types_allowed MOD_SEP '{' '}' { $$ = mk_node("ViewPathList", 2, $1, mk_atom("ViewPathListEmpty")); } -| MOD_SEP '{' '}' { $$ = mk_node("ViewPathList", 1, mk_atom("ViewPathListEmpty")); } -| path_no_types_allowed MOD_SEP '{' idents_or_self '}' { $$ = mk_node("ViewPathList", 2, $1, $4); } -| MOD_SEP '{' idents_or_self '}' { $$ = mk_node("ViewPathList", 1, $3); } -| path_no_types_allowed MOD_SEP '{' idents_or_self ',' '}' { $$ = mk_node("ViewPathList", 2, $1, $4); } -| MOD_SEP '{' idents_or_self ',' '}' { $$ = mk_node("ViewPathList", 1, $3); } -| path_no_types_allowed MOD_SEP '*' { $$ = mk_node("ViewPathGlob", 1, $1); } -| MOD_SEP '*' { $$ = mk_atom("ViewPathGlob"); } -| '*' { $$ = mk_atom("ViewPathGlob"); } -| '{' '}' { $$ = mk_atom("ViewPathListEmpty"); } -| '{' idents_or_self '}' { $$ = mk_node("ViewPathList", 1, $2); } -| '{' idents_or_self ',' '}' { $$ = mk_node("ViewPathList", 1, $2); } -| path_no_types_allowed AS ident { $$ = mk_node("ViewPathSimple", 2, $1, $3); } -; - -block_item -: item_fn -| item_unsafe_fn -| item_mod -| item_foreign_mod { $$ = mk_node("ItemForeignMod", 1, $1); } -| item_struct -| item_enum -| item_union -| item_trait -| item_impl -; - -maybe_ty_ascription -: ':' ty_sum { $$ = $2; } -| %empty { $$ = mk_none(); } -; - -maybe_init_expr -: '=' expr { $$ = $2; } -| %empty { $$ = mk_none(); } -; - -// structs -item_struct -: STRUCT ident generic_params maybe_where_clause struct_decl_args -{ - $$ = mk_node("ItemStruct", 4, $2, $3, $4, $5); -} -| STRUCT ident generic_params struct_tuple_args maybe_where_clause ';' -{ - $$ = mk_node("ItemStruct", 4, $2, $3, $4, $5); -} -| STRUCT ident generic_params maybe_where_clause ';' -{ - $$ = mk_node("ItemStruct", 3, $2, $3, $4); -} -; - -struct_decl_args -: '{' struct_decl_fields '}' { $$ = $2; } -| '{' struct_decl_fields ',' '}' { $$ = $2; } -; - -struct_tuple_args -: '(' struct_tuple_fields ')' { $$ = $2; } -| '(' struct_tuple_fields ',' ')' { $$ = $2; } -; - -struct_decl_fields -: struct_decl_field { $$ = mk_node("StructFields", 1, $1); } -| struct_decl_fields ',' struct_decl_field { $$ = ext_node($1, 1, $3); } -| %empty { $$ = mk_none(); } -; - -struct_decl_field -: attrs_and_vis ident ':' ty_sum { $$ = mk_node("StructField", 3, $1, $2, $4); } -; - -struct_tuple_fields -: struct_tuple_field { $$ = mk_node("StructFields", 1, $1); } -| struct_tuple_fields ',' struct_tuple_field { $$ = ext_node($1, 1, $3); } -| %empty { $$ = mk_none(); } -; - -struct_tuple_field -: attrs_and_vis ty_sum { $$ = mk_node("StructField", 2, $1, $2); } -; - -// enums -item_enum -: ENUM ident generic_params maybe_where_clause '{' enum_defs '}' { $$ = mk_node("ItemEnum", 0); } -| ENUM ident generic_params maybe_where_clause '{' enum_defs ',' '}' { $$ = mk_node("ItemEnum", 0); } -; - -enum_defs -: enum_def { $$ = mk_node("EnumDefs", 1, $1); } -| enum_defs ',' enum_def { $$ = ext_node($1, 1, $3); } -| %empty { $$ = mk_none(); } -; - -enum_def -: attrs_and_vis ident enum_args { $$ = mk_node("EnumDef", 3, $1, $2, $3); } -; - -enum_args -: '{' struct_decl_fields '}' { $$ = mk_node("EnumArgs", 1, $2); } -| '{' struct_decl_fields ',' '}' { $$ = mk_node("EnumArgs", 1, $2); } -| '(' maybe_ty_sums ')' { $$ = mk_node("EnumArgs", 1, $2); } -| '=' expr { $$ = mk_node("EnumArgs", 1, $2); } -| %empty { $$ = mk_none(); } -; - -// unions -item_union -: UNION ident generic_params maybe_where_clause '{' struct_decl_fields '}' { $$ = mk_node("ItemUnion", 0); } -| UNION ident generic_params maybe_where_clause '{' struct_decl_fields ',' '}' { $$ = mk_node("ItemUnion", 0); } - -item_mod -: MOD ident ';' { $$ = mk_node("ItemMod", 1, $2); } -| MOD ident '{' maybe_mod_items '}' { $$ = mk_node("ItemMod", 2, $2, $4); } -| MOD ident '{' inner_attrs maybe_mod_items '}' { $$ = mk_node("ItemMod", 3, $2, $4, $5); } -; - -item_foreign_mod -: EXTERN maybe_abi '{' maybe_foreign_items '}' { $$ = mk_node("ItemForeignMod", 1, $4); } -| EXTERN maybe_abi '{' inner_attrs maybe_foreign_items '}' { $$ = mk_node("ItemForeignMod", 2, $4, $5); } -; - -maybe_abi -: str -| %empty { $$ = mk_none(); } -; - -maybe_foreign_items -: foreign_items -| %empty { $$ = mk_none(); } -; - -foreign_items -: foreign_item { $$ = mk_node("ForeignItems", 1, $1); } -| foreign_items foreign_item { $$ = ext_node($1, 1, $2); } -; - -foreign_item -: attrs_and_vis STATIC item_foreign_static { $$ = mk_node("ForeignItem", 2, $1, $3); } -| attrs_and_vis item_foreign_fn { $$ = mk_node("ForeignItem", 2, $1, $2); } -| attrs_and_vis UNSAFE item_foreign_fn { $$ = mk_node("ForeignItem", 2, $1, $3); } -; - -item_foreign_static -: maybe_mut ident ':' ty ';' { $$ = mk_node("StaticItem", 3, $1, $2, $4); } -; - -item_foreign_fn -: FN ident generic_params fn_decl_allow_variadic maybe_where_clause ';' { $$ = mk_node("ForeignFn", 4, $2, $3, $4, $5); } -; - -fn_decl_allow_variadic -: fn_params_allow_variadic ret_ty { $$ = mk_node("FnDecl", 2, $1, $2); } -; - -fn_params_allow_variadic -: '(' ')' { $$ = mk_none(); } -| '(' params ')' { $$ = $2; } -| '(' params ',' ')' { $$ = $2; } -| '(' params ',' DOTDOTDOT ')' { $$ = $2; } -; - -visibility -: PUB { $$ = mk_atom("Public"); } -| %empty { $$ = mk_atom("Inherited"); } -; - -idents_or_self -: ident_or_self { $$ = mk_node("IdentsOrSelf", 1, $1); } -| idents_or_self AS ident { $$ = mk_node("IdentsOrSelf", 2, $1, $3); } -| idents_or_self ',' ident_or_self { $$ = ext_node($1, 1, $3); } -; - -ident_or_self -: ident -| SELF { $$ = mk_atom(yytext); } -; - -item_type -: TYPE ident generic_params maybe_where_clause '=' ty_sum ';' { $$ = mk_node("ItemTy", 4, $2, $3, $4, $6); } -; - -for_sized -: FOR '?' ident { $$ = mk_node("ForSized", 1, $3); } -| FOR ident '?' { $$ = mk_node("ForSized", 1, $2); } -| %empty { $$ = mk_none(); } -; - -item_trait -: maybe_unsafe TRAIT ident generic_params for_sized maybe_ty_param_bounds maybe_where_clause '{' maybe_trait_items '}' -{ - $$ = mk_node("ItemTrait", 7, $1, $3, $4, $5, $6, $7, $9); -} -; - -maybe_trait_items -: trait_items -| %empty { $$ = mk_none(); } -; - -trait_items -: trait_item { $$ = mk_node("TraitItems", 1, $1); } -| trait_items trait_item { $$ = ext_node($1, 1, $2); } -; - -trait_item -: trait_const -| trait_type -| trait_method -| maybe_outer_attrs item_macro { $$ = mk_node("TraitMacroItem", 2, $1, $2); } -; - -trait_const -: maybe_outer_attrs CONST ident maybe_ty_ascription maybe_const_default ';' { $$ = mk_node("ConstTraitItem", 4, $1, $3, $4, $5); } -; - -maybe_const_default -: '=' expr { $$ = mk_node("ConstDefault", 1, $2); } -| %empty { $$ = mk_none(); } -; - -trait_type -: maybe_outer_attrs TYPE ty_param ';' { $$ = mk_node("TypeTraitItem", 2, $1, $3); } -; - -maybe_unsafe -: UNSAFE { $$ = mk_atom("Unsafe"); } -| %empty { $$ = mk_none(); } -; - -maybe_default_maybe_unsafe -: DEFAULT UNSAFE { $$ = mk_atom("DefaultUnsafe"); } -| DEFAULT { $$ = mk_atom("Default"); } -| UNSAFE { $$ = mk_atom("Unsafe"); } -| %empty { $$ = mk_none(); } - -trait_method -: type_method { $$ = mk_node("Required", 1, $1); } -| method { $$ = mk_node("Provided", 1, $1); } -; - -type_method -: maybe_outer_attrs maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';' -{ - $$ = mk_node("TypeMethod", 6, $1, $2, $4, $5, $6, $7); -} -| maybe_outer_attrs CONST maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';' -{ - $$ = mk_node("TypeMethod", 6, $1, $3, $5, $6, $7, $8); -} -| maybe_outer_attrs maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';' -{ - $$ = mk_node("TypeMethod", 7, $1, $2, $4, $6, $7, $8, $9); -} -; - -method -: maybe_outer_attrs maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block -{ - $$ = mk_node("Method", 7, $1, $2, $4, $5, $6, $7, $8); -} -| maybe_outer_attrs CONST maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block -{ - $$ = mk_node("Method", 7, $1, $3, $5, $6, $7, $8, $9); -} -| maybe_outer_attrs maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block -{ - $$ = mk_node("Method", 8, $1, $2, $4, $6, $7, $8, $9, $10); -} -; - -impl_method -: attrs_and_vis maybe_default maybe_unsafe FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block -{ - $$ = mk_node("Method", 8, $1, $2, $3, $5, $6, $7, $8, $9); -} -| attrs_and_vis maybe_default CONST maybe_unsafe FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block -{ - $$ = mk_node("Method", 8, $1, $2, $4, $6, $7, $8, $9, $10); -} -| attrs_and_vis maybe_default maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block -{ - $$ = mk_node("Method", 9, $1, $2, $3, $5, $7, $8, $9, $10, $11); -} -; - -// There are two forms of impl: -// -// impl (<...>)? TY { ... } -// impl (<...>)? TRAIT for TY { ... } -// -// Unfortunately since TY can begin with '<' itself -- as part of a -// TyQualifiedPath type -- there's an s/r conflict when we see '<' after IMPL: -// should we reduce one of the early rules of TY (such as maybe_once) -// or shall we continue shifting into the generic_params list for the -// impl? -// -// The production parser disambiguates a different case here by -// permitting / requiring the user to provide parens around types when -// they are ambiguous with traits. We do the same here, regrettably, -// by splitting ty into ty and ty_prim. -item_impl -: maybe_default_maybe_unsafe IMPL generic_params ty_prim_sum maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}' -{ - $$ = mk_node("ItemImpl", 6, $1, $3, $4, $5, $7, $8); -} -| maybe_default_maybe_unsafe IMPL generic_params '(' ty ')' maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}' -{ - $$ = mk_node("ItemImpl", 6, $1, $3, 5, $6, $9, $10); -} -| maybe_default_maybe_unsafe IMPL generic_params trait_ref FOR ty_sum maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}' -{ - $$ = mk_node("ItemImpl", 6, $3, $4, $6, $7, $9, $10); -} -| maybe_default_maybe_unsafe IMPL generic_params '!' trait_ref FOR ty_sum maybe_where_clause '{' maybe_inner_attrs maybe_impl_items '}' -{ - $$ = mk_node("ItemImplNeg", 7, $1, $3, $5, $7, $8, $10, $11); -} -| maybe_default_maybe_unsafe IMPL generic_params trait_ref FOR DOTDOT '{' '}' -{ - $$ = mk_node("ItemImplDefault", 3, $1, $3, $4); -} -| maybe_default_maybe_unsafe IMPL generic_params '!' trait_ref FOR DOTDOT '{' '}' -{ - $$ = mk_node("ItemImplDefaultNeg", 3, $1, $3, $4); -} -; - -maybe_impl_items -: impl_items -| %empty { $$ = mk_none(); } -; - -impl_items -: impl_item { $$ = mk_node("ImplItems", 1, $1); } -| impl_item impl_items { $$ = ext_node($1, 1, $2); } -; - -impl_item -: impl_method -| attrs_and_vis item_macro { $$ = mk_node("ImplMacroItem", 2, $1, $2); } -| impl_const -| impl_type -; - -maybe_default -: DEFAULT { $$ = mk_atom("Default"); } -| %empty { $$ = mk_none(); } -; - -impl_const -: attrs_and_vis maybe_default item_const { $$ = mk_node("ImplConst", 3, $1, $2, $3); } -; - -impl_type -: attrs_and_vis maybe_default TYPE ident generic_params '=' ty_sum ';' { $$ = mk_node("ImplType", 5, $1, $2, $4, $5, $7); } -; - -item_fn -: FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block -{ - $$ = mk_node("ItemFn", 5, $2, $3, $4, $5, $6); -} -| CONST FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block -{ - $$ = mk_node("ItemFn", 5, $3, $4, $5, $6, $7); -} -; - -item_unsafe_fn -: UNSAFE FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block -{ - $$ = mk_node("ItemUnsafeFn", 5, $3, $4, $5, $6, $7); -} -| CONST UNSAFE FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block -{ - $$ = mk_node("ItemUnsafeFn", 5, $4, $5, $6, $7, $8); -} -| UNSAFE EXTERN maybe_abi FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block -{ - $$ = mk_node("ItemUnsafeFn", 6, $3, $5, $6, $7, $8, $9); -} -; - -fn_decl -: fn_params ret_ty { $$ = mk_node("FnDecl", 2, $1, $2); } -; - -fn_decl_with_self -: fn_params_with_self ret_ty { $$ = mk_node("FnDecl", 2, $1, $2); } -; - -fn_decl_with_self_allow_anon_params -: fn_anon_params_with_self ret_ty { $$ = mk_node("FnDecl", 2, $1, $2); } -; - -fn_params -: '(' maybe_params ')' { $$ = $2; } -; - -fn_anon_params -: '(' anon_param anon_params_allow_variadic_tail ')' { $$ = ext_node($2, 1, $3); } -| '(' ')' { $$ = mk_none(); } -; - -fn_params_with_self -: '(' maybe_mut SELF maybe_ty_ascription maybe_comma_params ')' { $$ = mk_node("SelfLower", 3, $2, $4, $5); } -| '(' '&' maybe_mut SELF maybe_ty_ascription maybe_comma_params ')' { $$ = mk_node("SelfRegion", 3, $3, $5, $6); } -| '(' '&' lifetime maybe_mut SELF maybe_ty_ascription maybe_comma_params ')' { $$ = mk_node("SelfRegion", 4, $3, $4, $6, $7); } -| '(' maybe_params ')' { $$ = mk_node("SelfStatic", 1, $2); } -; - -fn_anon_params_with_self -: '(' maybe_mut SELF maybe_ty_ascription maybe_comma_anon_params ')' { $$ = mk_node("SelfLower", 3, $2, $4, $5); } -| '(' '&' maybe_mut SELF maybe_ty_ascription maybe_comma_anon_params ')' { $$ = mk_node("SelfRegion", 3, $3, $5, $6); } -| '(' '&' lifetime maybe_mut SELF maybe_ty_ascription maybe_comma_anon_params ')' { $$ = mk_node("SelfRegion", 4, $3, $4, $6, $7); } -| '(' maybe_anon_params ')' { $$ = mk_node("SelfStatic", 1, $2); } -; - -maybe_params -: params -| params ',' -| %empty { $$ = mk_none(); } -; - -params -: param { $$ = mk_node("Args", 1, $1); } -| params ',' param { $$ = ext_node($1, 1, $3); } -; - -param -: pat ':' ty_sum { $$ = mk_node("Arg", 2, $1, $3); } -; - -inferrable_params -: inferrable_param { $$ = mk_node("InferrableParams", 1, $1); } -| inferrable_params ',' inferrable_param { $$ = ext_node($1, 1, $3); } -; - -inferrable_param -: pat maybe_ty_ascription { $$ = mk_node("InferrableParam", 2, $1, $2); } -; - -maybe_comma_params -: ',' { $$ = mk_none(); } -| ',' params { $$ = $2; } -| ',' params ',' { $$ = $2; } -| %empty { $$ = mk_none(); } -; - -maybe_comma_anon_params -: ',' { $$ = mk_none(); } -| ',' anon_params { $$ = $2; } -| ',' anon_params ',' { $$ = $2; } -| %empty { $$ = mk_none(); } -; - -maybe_anon_params -: anon_params -| anon_params ',' -| %empty { $$ = mk_none(); } -; - -anon_params -: anon_param { $$ = mk_node("Args", 1, $1); } -| anon_params ',' anon_param { $$ = ext_node($1, 1, $3); } -; - -// anon means it's allowed to be anonymous (type-only), but it can -// still have a name -anon_param -: named_arg ':' ty { $$ = mk_node("Arg", 2, $1, $3); } -| ty -; - -anon_params_allow_variadic_tail -: ',' DOTDOTDOT { $$ = mk_none(); } -| ',' anon_param anon_params_allow_variadic_tail { $$ = mk_node("Args", 2, $2, $3); } -| %empty { $$ = mk_none(); } -; - -named_arg -: ident -| UNDERSCORE { $$ = mk_atom("PatWild"); } -| '&' ident { $$ = $2; } -| '&' UNDERSCORE { $$ = mk_atom("PatWild"); } -| ANDAND ident { $$ = $2; } -| ANDAND UNDERSCORE { $$ = mk_atom("PatWild"); } -| MUT ident { $$ = $2; } -; - -ret_ty -: RARROW '!' { $$ = mk_none(); } -| RARROW ty { $$ = mk_node("ret-ty", 1, $2); } -| %prec IDENT %empty { $$ = mk_none(); } -; - -generic_params -: '<' '>' { $$ = mk_node("Generics", 2, mk_none(), mk_none()); } -| '<' lifetimes '>' { $$ = mk_node("Generics", 2, $2, mk_none()); } -| '<' lifetimes ',' '>' { $$ = mk_node("Generics", 2, $2, mk_none()); } -| '<' lifetimes SHR { push_back('>'); $$ = mk_node("Generics", 2, $2, mk_none()); } -| '<' lifetimes ',' SHR { push_back('>'); $$ = mk_node("Generics", 2, $2, mk_none()); } -| '<' lifetimes ',' ty_params '>' { $$ = mk_node("Generics", 2, $2, $4); } -| '<' lifetimes ',' ty_params ',' '>' { $$ = mk_node("Generics", 2, $2, $4); } -| '<' lifetimes ',' ty_params SHR { push_back('>'); $$ = mk_node("Generics", 2, $2, $4); } -| '<' lifetimes ',' ty_params ',' SHR { push_back('>'); $$ = mk_node("Generics", 2, $2, $4); } -| '<' ty_params '>' { $$ = mk_node("Generics", 2, mk_none(), $2); } -| '<' ty_params ',' '>' { $$ = mk_node("Generics", 2, mk_none(), $2); } -| '<' ty_params SHR { push_back('>'); $$ = mk_node("Generics", 2, mk_none(), $2); } -| '<' ty_params ',' SHR { push_back('>'); $$ = mk_node("Generics", 2, mk_none(), $2); } -| %empty { $$ = mk_none(); } -; - -maybe_where_clause -: %empty { $$ = mk_none(); } -| where_clause -; - -where_clause -: WHERE where_predicates { $$ = mk_node("WhereClause", 1, $2); } -| WHERE where_predicates ',' { $$ = mk_node("WhereClause", 1, $2); } -; - -where_predicates -: where_predicate { $$ = mk_node("WherePredicates", 1, $1); } -| where_predicates ',' where_predicate { $$ = ext_node($1, 1, $3); } -; - -where_predicate -: maybe_for_lifetimes lifetime ':' bounds { $$ = mk_node("WherePredicate", 3, $1, $2, $4); } -| maybe_for_lifetimes ty ':' ty_param_bounds { $$ = mk_node("WherePredicate", 3, $1, $2, $4); } -; - -maybe_for_lifetimes -: FOR '<' lifetimes '>' { $$ = mk_none(); } -| %prec FORTYPE %empty { $$ = mk_none(); } - -ty_params -: ty_param { $$ = mk_node("TyParams", 1, $1); } -| ty_params ',' ty_param { $$ = ext_node($1, 1, $3); } -; - -// A path with no type parameters; e.g. `foo::bar::Baz` -// -// These show up in 'use' view-items, because these are processed -// without respect to types. -path_no_types_allowed -: ident { $$ = mk_node("ViewPath", 1, $1); } -| MOD_SEP ident { $$ = mk_node("ViewPath", 1, $2); } -| SELF { $$ = mk_node("ViewPath", 1, mk_atom("Self")); } -| MOD_SEP SELF { $$ = mk_node("ViewPath", 1, mk_atom("Self")); } -| SUPER { $$ = mk_node("ViewPath", 1, mk_atom("Super")); } -| MOD_SEP SUPER { $$ = mk_node("ViewPath", 1, mk_atom("Super")); } -| path_no_types_allowed MOD_SEP ident { $$ = ext_node($1, 1, $3); } -; - -// A path with a lifetime and type parameters, with no double colons -// before the type parameters; e.g. `foo::bar<'a>::Baz<T>` -// -// These show up in "trait references", the components of -// type-parameter bounds lists, as well as in the prefix of the -// path_generic_args_and_bounds rule, which is the full form of a -// named typed expression. -// -// They do not have (nor need) an extra '::' before '<' because -// unlike in expr context, there are no "less-than" type exprs to -// be ambiguous with. -path_generic_args_without_colons -: %prec IDENT - ident { $$ = mk_node("components", 1, $1); } -| %prec IDENT - ident generic_args { $$ = mk_node("components", 2, $1, $2); } -| %prec IDENT - ident '(' maybe_ty_sums ')' ret_ty { $$ = mk_node("components", 2, $1, $3); } -| %prec IDENT - path_generic_args_without_colons MOD_SEP ident { $$ = ext_node($1, 1, $3); } -| %prec IDENT - path_generic_args_without_colons MOD_SEP ident generic_args { $$ = ext_node($1, 2, $3, $4); } -| %prec IDENT - path_generic_args_without_colons MOD_SEP ident '(' maybe_ty_sums ')' ret_ty { $$ = ext_node($1, 2, $3, $5); } -; - -generic_args -: '<' generic_values '>' { $$ = $2; } -| '<' generic_values SHR { push_back('>'); $$ = $2; } -| '<' generic_values GE { push_back('='); $$ = $2; } -| '<' generic_values SHREQ { push_back('>'); push_back('='); $$ = $2; } -// If generic_args starts with "<<", the first arg must be a -// TyQualifiedPath because that's the only type that can start with a -// '<'. This rule parses that as the first ty_sum and then continues -// with the rest of generic_values. -| SHL ty_qualified_path_and_generic_values '>' { $$ = $2; } -| SHL ty_qualified_path_and_generic_values SHR { push_back('>'); $$ = $2; } -| SHL ty_qualified_path_and_generic_values GE { push_back('='); $$ = $2; } -| SHL ty_qualified_path_and_generic_values SHREQ { push_back('>'); push_back('='); $$ = $2; } -; - -generic_values -: maybe_ty_sums_and_or_bindings { $$ = mk_node("GenericValues", 1, $1); } -; - -maybe_ty_sums_and_or_bindings -: ty_sums -| ty_sums ',' -| ty_sums ',' bindings { $$ = mk_node("TySumsAndBindings", 2, $1, $3); } -| bindings -| bindings ',' -| %empty { $$ = mk_none(); } -; - -maybe_bindings -: ',' bindings { $$ = $2; } -| %empty { $$ = mk_none(); } -; - -//////////////////////////////////////////////////////////////////////// -// Part 2: Patterns -//////////////////////////////////////////////////////////////////////// - -pat -: UNDERSCORE { $$ = mk_atom("PatWild"); } -| '&' pat { $$ = mk_node("PatRegion", 1, $2); } -| '&' MUT pat { $$ = mk_node("PatRegion", 1, $3); } -| ANDAND pat { $$ = mk_node("PatRegion", 1, mk_node("PatRegion", 1, $2)); } -| '(' ')' { $$ = mk_atom("PatUnit"); } -| '(' pat_tup ')' { $$ = mk_node("PatTup", 1, $2); } -| '[' pat_vec ']' { $$ = mk_node("PatVec", 1, $2); } -| lit_or_path -| lit_or_path DOTDOTDOT lit_or_path { $$ = mk_node("PatRange", 2, $1, $3); } -| path_expr '{' pat_struct '}' { $$ = mk_node("PatStruct", 2, $1, $3); } -| path_expr '(' ')' { $$ = mk_node("PatEnum", 2, $1, mk_none()); } -| path_expr '(' pat_tup ')' { $$ = mk_node("PatEnum", 2, $1, $3); } -| path_expr '!' maybe_ident delimited_token_trees { $$ = mk_node("PatMac", 3, $1, $3, $4); } -| binding_mode ident { $$ = mk_node("PatIdent", 2, $1, $2); } -| ident '@' pat { $$ = mk_node("PatIdent", 3, mk_node("BindByValue", 1, mk_atom("MutImmutable")), $1, $3); } -| binding_mode ident '@' pat { $$ = mk_node("PatIdent", 3, $1, $2, $4); } -| BOX pat { $$ = mk_node("PatUniq", 1, $2); } -| '<' ty_sum maybe_as_trait_ref '>' MOD_SEP ident { $$ = mk_node("PatQualifiedPath", 3, $2, $3, $6); } -| SHL ty_sum maybe_as_trait_ref '>' MOD_SEP ident maybe_as_trait_ref '>' MOD_SEP ident -{ - $$ = mk_node("PatQualifiedPath", 3, mk_node("PatQualifiedPath", 3, $2, $3, $6), $7, $10); -} -; - -pats_or -: pat { $$ = mk_node("Pats", 1, $1); } -| pats_or '|' pat { $$ = ext_node($1, 1, $3); } -; - -binding_mode -: REF { $$ = mk_node("BindByRef", 1, mk_atom("MutImmutable")); } -| REF MUT { $$ = mk_node("BindByRef", 1, mk_atom("MutMutable")); } -| MUT { $$ = mk_node("BindByValue", 1, mk_atom("MutMutable")); } -; - -lit_or_path -: path_expr { $$ = mk_node("PatLit", 1, $1); } -| lit { $$ = mk_node("PatLit", 1, $1); } -| '-' lit { $$ = mk_node("PatLit", 1, $2); } -; - -pat_field -: ident { $$ = mk_node("PatField", 1, $1); } -| binding_mode ident { $$ = mk_node("PatField", 2, $1, $2); } -| BOX ident { $$ = mk_node("PatField", 2, mk_atom("box"), $2); } -| BOX binding_mode ident { $$ = mk_node("PatField", 3, mk_atom("box"), $2, $3); } -| ident ':' pat { $$ = mk_node("PatField", 2, $1, $3); } -| binding_mode ident ':' pat { $$ = mk_node("PatField", 3, $1, $2, $4); } -| LIT_INTEGER ':' pat { $$ = mk_node("PatField", 2, mk_atom(yytext), $3); } -; - -pat_fields -: pat_field { $$ = mk_node("PatFields", 1, $1); } -| pat_fields ',' pat_field { $$ = ext_node($1, 1, $3); } -; - -pat_struct -: pat_fields { $$ = mk_node("PatStruct", 2, $1, mk_atom("false")); } -| pat_fields ',' { $$ = mk_node("PatStruct", 2, $1, mk_atom("false")); } -| pat_fields ',' DOTDOT { $$ = mk_node("PatStruct", 2, $1, mk_atom("true")); } -| DOTDOT { $$ = mk_node("PatStruct", 1, mk_atom("true")); } -| %empty { $$ = mk_node("PatStruct", 1, mk_none()); } -; - -pat_tup -: pat_tup_elts { $$ = mk_node("PatTup", 2, $1, mk_none()); } -| pat_tup_elts ',' { $$ = mk_node("PatTup", 2, $1, mk_none()); } -| pat_tup_elts DOTDOT { $$ = mk_node("PatTup", 2, $1, mk_none()); } -| pat_tup_elts ',' DOTDOT { $$ = mk_node("PatTup", 2, $1, mk_none()); } -| pat_tup_elts DOTDOT ',' pat_tup_elts { $$ = mk_node("PatTup", 2, $1, $4); } -| pat_tup_elts DOTDOT ',' pat_tup_elts ',' { $$ = mk_node("PatTup", 2, $1, $4); } -| pat_tup_elts ',' DOTDOT ',' pat_tup_elts { $$ = mk_node("PatTup", 2, $1, $5); } -| pat_tup_elts ',' DOTDOT ',' pat_tup_elts ',' { $$ = mk_node("PatTup", 2, $1, $5); } -| DOTDOT ',' pat_tup_elts { $$ = mk_node("PatTup", 2, mk_none(), $3); } -| DOTDOT ',' pat_tup_elts ',' { $$ = mk_node("PatTup", 2, mk_none(), $3); } -| DOTDOT { $$ = mk_node("PatTup", 2, mk_none(), mk_none()); } -; - -pat_tup_elts -: pat { $$ = mk_node("PatTupElts", 1, $1); } -| pat_tup_elts ',' pat { $$ = ext_node($1, 1, $3); } -; - -pat_vec -: pat_vec_elts { $$ = mk_node("PatVec", 2, $1, mk_none()); } -| pat_vec_elts ',' { $$ = mk_node("PatVec", 2, $1, mk_none()); } -| pat_vec_elts DOTDOT { $$ = mk_node("PatVec", 2, $1, mk_none()); } -| pat_vec_elts ',' DOTDOT { $$ = mk_node("PatVec", 2, $1, mk_none()); } -| pat_vec_elts DOTDOT ',' pat_vec_elts { $$ = mk_node("PatVec", 2, $1, $4); } -| pat_vec_elts DOTDOT ',' pat_vec_elts ',' { $$ = mk_node("PatVec", 2, $1, $4); } -| pat_vec_elts ',' DOTDOT ',' pat_vec_elts { $$ = mk_node("PatVec", 2, $1, $5); } -| pat_vec_elts ',' DOTDOT ',' pat_vec_elts ',' { $$ = mk_node("PatVec", 2, $1, $5); } -| DOTDOT ',' pat_vec_elts { $$ = mk_node("PatVec", 2, mk_none(), $3); } -| DOTDOT ',' pat_vec_elts ',' { $$ = mk_node("PatVec", 2, mk_none(), $3); } -| DOTDOT { $$ = mk_node("PatVec", 2, mk_none(), mk_none()); } -| %empty { $$ = mk_node("PatVec", 2, mk_none(), mk_none()); } -; - -pat_vec_elts -: pat { $$ = mk_node("PatVecElts", 1, $1); } -| pat_vec_elts ',' pat { $$ = ext_node($1, 1, $3); } -; - -//////////////////////////////////////////////////////////////////////// -// Part 3: Types -//////////////////////////////////////////////////////////////////////// - -ty -: ty_prim -| ty_closure -| '<' ty_sum maybe_as_trait_ref '>' MOD_SEP ident { $$ = mk_node("TyQualifiedPath", 3, $2, $3, $6); } -| SHL ty_sum maybe_as_trait_ref '>' MOD_SEP ident maybe_as_trait_ref '>' MOD_SEP ident { $$ = mk_node("TyQualifiedPath", 3, mk_node("TyQualifiedPath", 3, $2, $3, $6), $7, $10); } -| '(' ty_sums ')' { $$ = mk_node("TyTup", 1, $2); } -| '(' ty_sums ',' ')' { $$ = mk_node("TyTup", 1, $2); } -| '(' ')' { $$ = mk_atom("TyNil"); } -; - -ty_prim -: %prec IDENT path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("global", 1, mk_atom("false")), $1); } -| %prec IDENT MOD_SEP path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("global", 1, mk_atom("true")), $2); } -| %prec IDENT SELF MOD_SEP path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("self", 1, mk_atom("true")), $3); } -| %prec IDENT path_generic_args_without_colons '!' maybe_ident delimited_token_trees { $$ = mk_node("TyMacro", 3, $1, $3, $4); } -| %prec IDENT MOD_SEP path_generic_args_without_colons '!' maybe_ident delimited_token_trees { $$ = mk_node("TyMacro", 3, $2, $4, $5); } -| BOX ty { $$ = mk_node("TyBox", 1, $2); } -| '*' maybe_mut_or_const ty { $$ = mk_node("TyPtr", 2, $2, $3); } -| '&' ty { $$ = mk_node("TyRptr", 2, mk_atom("MutImmutable"), $2); } -| '&' MUT ty { $$ = mk_node("TyRptr", 2, mk_atom("MutMutable"), $3); } -| ANDAND ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 2, mk_atom("MutImmutable"), $2)); } -| ANDAND MUT ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 2, mk_atom("MutMutable"), $3)); } -| '&' lifetime maybe_mut ty { $$ = mk_node("TyRptr", 3, $2, $3, $4); } -| ANDAND lifetime maybe_mut ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 3, $2, $3, $4)); } -| '[' ty ']' { $$ = mk_node("TyVec", 1, $2); } -| '[' ty ',' DOTDOT expr ']' { $$ = mk_node("TyFixedLengthVec", 2, $2, $5); } -| '[' ty ';' expr ']' { $$ = mk_node("TyFixedLengthVec", 2, $2, $4); } -| TYPEOF '(' expr ')' { $$ = mk_node("TyTypeof", 1, $3); } -| UNDERSCORE { $$ = mk_atom("TyInfer"); } -| ty_bare_fn -| for_in_type -; - -ty_bare_fn -: FN ty_fn_decl { $$ = $2; } -| UNSAFE FN ty_fn_decl { $$ = $3; } -| EXTERN maybe_abi FN ty_fn_decl { $$ = $4; } -| UNSAFE EXTERN maybe_abi FN ty_fn_decl { $$ = $5; } -; - -ty_fn_decl -: generic_params fn_anon_params ret_ty { $$ = mk_node("TyFnDecl", 3, $1, $2, $3); } -; - -ty_closure -: UNSAFE '|' anon_params '|' maybe_bounds ret_ty { $$ = mk_node("TyClosure", 3, $3, $5, $6); } -| '|' anon_params '|' maybe_bounds ret_ty { $$ = mk_node("TyClosure", 3, $2, $4, $5); } -| UNSAFE OROR maybe_bounds ret_ty { $$ = mk_node("TyClosure", 2, $3, $4); } -| OROR maybe_bounds ret_ty { $$ = mk_node("TyClosure", 2, $2, $3); } -; - -for_in_type -: FOR '<' maybe_lifetimes '>' for_in_type_suffix { $$ = mk_node("ForInType", 2, $3, $5); } -; - -for_in_type_suffix -: ty_bare_fn -| trait_ref -| ty_closure -; - -maybe_mut -: MUT { $$ = mk_atom("MutMutable"); } -| %prec MUT %empty { $$ = mk_atom("MutImmutable"); } -; - -maybe_mut_or_const -: MUT { $$ = mk_atom("MutMutable"); } -| CONST { $$ = mk_atom("MutImmutable"); } -| %empty { $$ = mk_atom("MutImmutable"); } -; - -ty_qualified_path_and_generic_values -: ty_qualified_path maybe_bindings -{ - $$ = mk_node("GenericValues", 3, mk_none(), mk_node("TySums", 1, mk_node("TySum", 1, $1)), $2); -} -| ty_qualified_path ',' ty_sums maybe_bindings -{ - $$ = mk_node("GenericValues", 3, mk_none(), mk_node("TySums", 2, $1, $3), $4); -} -; - -ty_qualified_path -: ty_sum AS trait_ref '>' MOD_SEP ident { $$ = mk_node("TyQualifiedPath", 3, $1, $3, $6); } -| ty_sum AS trait_ref '>' MOD_SEP ident '+' ty_param_bounds { $$ = mk_node("TyQualifiedPath", 3, $1, $3, $6); } -; - -maybe_ty_sums -: ty_sums -| ty_sums ',' -| %empty { $$ = mk_none(); } -; - -ty_sums -: ty_sum { $$ = mk_node("TySums", 1, $1); } -| ty_sums ',' ty_sum { $$ = ext_node($1, 1, $3); } -; - -ty_sum -: ty_sum_elt { $$ = mk_node("TySum", 1, $1); } -| ty_sum '+' ty_sum_elt { $$ = ext_node($1, 1, $3); } -; - -ty_sum_elt -: ty -| lifetime -; - -ty_prim_sum -: ty_prim_sum_elt { $$ = mk_node("TySum", 1, $1); } -| ty_prim_sum '+' ty_prim_sum_elt { $$ = ext_node($1, 1, $3); } -; - -ty_prim_sum_elt -: ty_prim -| lifetime -; - -maybe_ty_param_bounds -: ':' ty_param_bounds { $$ = $2; } -| %empty { $$ = mk_none(); } -; - -ty_param_bounds -: boundseq -| %empty { $$ = mk_none(); } -; - -boundseq -: polybound -| boundseq '+' polybound { $$ = ext_node($1, 1, $3); } -; - -polybound -: FOR '<' maybe_lifetimes '>' bound { $$ = mk_node("PolyBound", 2, $3, $5); } -| bound -| '?' FOR '<' maybe_lifetimes '>' bound { $$ = mk_node("PolyBound", 2, $4, $6); } -| '?' bound { $$ = $2; } -; - -bindings -: binding { $$ = mk_node("Bindings", 1, $1); } -| bindings ',' binding { $$ = ext_node($1, 1, $3); } -; - -binding -: ident '=' ty { mk_node("Binding", 2, $1, $3); } -; - -ty_param -: ident maybe_ty_param_bounds maybe_ty_default { $$ = mk_node("TyParam", 3, $1, $2, $3); } -| ident '?' ident maybe_ty_param_bounds maybe_ty_default { $$ = mk_node("TyParam", 4, $1, $3, $4, $5); } -; - -maybe_bounds -: %prec SHIFTPLUS - ':' bounds { $$ = $2; } -| %prec SHIFTPLUS %empty { $$ = mk_none(); } -; - -bounds -: bound { $$ = mk_node("bounds", 1, $1); } -| bounds '+' bound { $$ = ext_node($1, 1, $3); } -; - -bound -: lifetime -| trait_ref -; - -maybe_ltbounds -: %prec SHIFTPLUS - ':' ltbounds { $$ = $2; } -| %empty { $$ = mk_none(); } -; - -ltbounds -: lifetime { $$ = mk_node("ltbounds", 1, $1); } -| ltbounds '+' lifetime { $$ = ext_node($1, 1, $3); } -; - -maybe_ty_default -: '=' ty_sum { $$ = mk_node("TyDefault", 1, $2); } -| %empty { $$ = mk_none(); } -; - -maybe_lifetimes -: lifetimes -| lifetimes ',' -| %empty { $$ = mk_none(); } -; - -lifetimes -: lifetime_and_bounds { $$ = mk_node("Lifetimes", 1, $1); } -| lifetimes ',' lifetime_and_bounds { $$ = ext_node($1, 1, $3); } -; - -lifetime_and_bounds -: LIFETIME maybe_ltbounds { $$ = mk_node("lifetime", 2, mk_atom(yytext), $2); } -| STATIC_LIFETIME { $$ = mk_atom("static_lifetime"); } -; - -lifetime -: LIFETIME { $$ = mk_node("lifetime", 1, mk_atom(yytext)); } -| STATIC_LIFETIME { $$ = mk_atom("static_lifetime"); } -; - -trait_ref -: %prec IDENT path_generic_args_without_colons -| %prec IDENT MOD_SEP path_generic_args_without_colons { $$ = $2; } -; - -//////////////////////////////////////////////////////////////////////// -// Part 4: Blocks, statements, and expressions -//////////////////////////////////////////////////////////////////////// - -inner_attrs_and_block -: '{' maybe_inner_attrs maybe_stmts '}' { $$ = mk_node("ExprBlock", 2, $2, $3); } -; - -block -: '{' maybe_stmts '}' { $$ = mk_node("ExprBlock", 1, $2); } -; - -maybe_stmts -: stmts -| stmts nonblock_expr { $$ = ext_node($1, 1, $2); } -| nonblock_expr -| %empty { $$ = mk_none(); } -; - -// There are two sub-grammars within a "stmts: exprs" derivation -// depending on whether each stmt-expr is a block-expr form; this is to -// handle the "semicolon rule" for stmt sequencing that permits -// writing -// -// if foo { bar } 10 -// -// as a sequence of two stmts (one if-expr stmt, one lit-10-expr -// stmt). Unfortunately by permitting juxtaposition of exprs in -// sequence like that, the non-block expr grammar has to have a -// second limited sub-grammar that excludes the prefix exprs that -// are ambiguous with binops. That is to say: -// -// {10} - 1 -// -// should parse as (progn (progn 10) (- 1)) not (- (progn 10) 1), that -// is to say, two statements rather than one, at least according to -// the mainline rust parser. -// -// So we wind up with a 3-way split in exprs that occur in stmt lists: -// block, nonblock-prefix, and nonblock-nonprefix. -// -// In non-stmts contexts, expr can relax this trichotomy. - -stmts -: stmt { $$ = mk_node("stmts", 1, $1); } -| stmts stmt { $$ = ext_node($1, 1, $2); } -; - -stmt -: maybe_outer_attrs let { $$ = $2; } -| stmt_item -| PUB stmt_item { $$ = $2; } -| outer_attrs stmt_item { $$ = $2; } -| outer_attrs PUB stmt_item { $$ = $3; } -| full_block_expr -| maybe_outer_attrs block { $$ = $2; } -| nonblock_expr ';' -| outer_attrs nonblock_expr ';' { $$ = $2; } -| ';' { $$ = mk_none(); } -; - -maybe_exprs -: exprs -| exprs ',' -| %empty { $$ = mk_none(); } -; - -maybe_expr -: expr -| %empty { $$ = mk_none(); } -; - -exprs -: expr { $$ = mk_node("exprs", 1, $1); } -| exprs ',' expr { $$ = ext_node($1, 1, $3); } -; - -path_expr -: path_generic_args_with_colons -| MOD_SEP path_generic_args_with_colons { $$ = $2; } -| SELF MOD_SEP path_generic_args_with_colons { $$ = mk_node("SelfPath", 1, $3); } -; - -// A path with a lifetime and type parameters with double colons before -// the type parameters; e.g. `foo::bar::<'a>::Baz::<T>` -// -// These show up in expr context, in order to disambiguate from "less-than" -// expressions. -path_generic_args_with_colons -: ident { $$ = mk_node("components", 1, $1); } -| SUPER { $$ = mk_atom("Super"); } -| path_generic_args_with_colons MOD_SEP ident { $$ = ext_node($1, 1, $3); } -| path_generic_args_with_colons MOD_SEP SUPER { $$ = ext_node($1, 1, mk_atom("Super")); } -| path_generic_args_with_colons MOD_SEP generic_args { $$ = ext_node($1, 1, $3); } -; - -// the braces-delimited macro is a block_expr so it doesn't appear here -macro_expr -: path_expr '!' maybe_ident parens_delimited_token_trees { $$ = mk_node("MacroExpr", 3, $1, $3, $4); } -| path_expr '!' maybe_ident brackets_delimited_token_trees { $$ = mk_node("MacroExpr", 3, $1, $3, $4); } -; - -nonblock_expr -: lit { $$ = mk_node("ExprLit", 1, $1); } -| %prec IDENT - path_expr { $$ = mk_node("ExprPath", 1, $1); } -| SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } -| macro_expr { $$ = mk_node("ExprMac", 1, $1); } -| path_expr '{' struct_expr_fields '}' { $$ = mk_node("ExprStruct", 2, $1, $3); } -| nonblock_expr '?' { $$ = mk_node("ExprTry", 1, $1); } -| nonblock_expr '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } -| nonblock_expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } -| nonblock_expr '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } -| nonblock_expr '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 2, $1, $3); } -| '[' vec_expr ']' { $$ = mk_node("ExprVec", 1, $2); } -| '(' maybe_exprs ')' { $$ = mk_node("ExprParen", 1, $2); } -| CONTINUE { $$ = mk_node("ExprAgain", 0); } -| CONTINUE lifetime { $$ = mk_node("ExprAgain", 1, $2); } -| RETURN { $$ = mk_node("ExprRet", 0); } -| RETURN expr { $$ = mk_node("ExprRet", 1, $2); } -| BREAK { $$ = mk_node("ExprBreak", 0); } -| BREAK lifetime { $$ = mk_node("ExprBreak", 1, $2); } -| YIELD { $$ = mk_node("ExprYield", 0); } -| YIELD expr { $$ = mk_node("ExprYield", 1, $2); } -| nonblock_expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); } -| nonblock_expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } -| nonblock_expr SHREQ expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); } -| nonblock_expr MINUSEQ expr { $$ = mk_node("ExprAssignSub", 2, $1, $3); } -| nonblock_expr ANDEQ expr { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); } -| nonblock_expr OREQ expr { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); } -| nonblock_expr PLUSEQ expr { $$ = mk_node("ExprAssignAdd", 2, $1, $3); } -| nonblock_expr STAREQ expr { $$ = mk_node("ExprAssignMul", 2, $1, $3); } -| nonblock_expr SLASHEQ expr { $$ = mk_node("ExprAssignDiv", 2, $1, $3); } -| nonblock_expr CARETEQ expr { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); } -| nonblock_expr PERCENTEQ expr { $$ = mk_node("ExprAssignRem", 2, $1, $3); } -| nonblock_expr OROR expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); } -| nonblock_expr ANDAND expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); } -| nonblock_expr EQEQ expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); } -| nonblock_expr NE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); } -| nonblock_expr '<' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); } -| nonblock_expr '>' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); } -| nonblock_expr LE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); } -| nonblock_expr GE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); } -| nonblock_expr '|' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); } -| nonblock_expr '^' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); } -| nonblock_expr '&' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); } -| nonblock_expr SHL expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); } -| nonblock_expr SHR expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); } -| nonblock_expr '+' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); } -| nonblock_expr '-' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); } -| nonblock_expr '*' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); } -| nonblock_expr '/' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); } -| nonblock_expr '%' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); } -| nonblock_expr DOTDOT { $$ = mk_node("ExprRange", 2, $1, mk_none()); } -| nonblock_expr DOTDOT expr { $$ = mk_node("ExprRange", 2, $1, $3); } -| DOTDOT expr { $$ = mk_node("ExprRange", 2, mk_none(), $2); } -| DOTDOT { $$ = mk_node("ExprRange", 2, mk_none(), mk_none()); } -| nonblock_expr AS ty { $$ = mk_node("ExprCast", 2, $1, $3); } -| nonblock_expr ':' ty { $$ = mk_node("ExprTypeAscr", 2, $1, $3); } -| BOX expr { $$ = mk_node("ExprBox", 1, $2); } -| expr_qualified_path -| nonblock_prefix_expr -; - -expr -: lit { $$ = mk_node("ExprLit", 1, $1); } -| %prec IDENT - path_expr { $$ = mk_node("ExprPath", 1, $1); } -| SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } -| macro_expr { $$ = mk_node("ExprMac", 1, $1); } -| path_expr '{' struct_expr_fields '}' { $$ = mk_node("ExprStruct", 2, $1, $3); } -| expr '?' { $$ = mk_node("ExprTry", 1, $1); } -| expr '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } -| expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } -| expr '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } -| expr '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 2, $1, $3); } -| '(' maybe_exprs ')' { $$ = mk_node("ExprParen", 1, $2); } -| '[' vec_expr ']' { $$ = mk_node("ExprVec", 1, $2); } -| CONTINUE { $$ = mk_node("ExprAgain", 0); } -| CONTINUE ident { $$ = mk_node("ExprAgain", 1, $2); } -| RETURN { $$ = mk_node("ExprRet", 0); } -| RETURN expr { $$ = mk_node("ExprRet", 1, $2); } -| BREAK { $$ = mk_node("ExprBreak", 0); } -| BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } -| YIELD { $$ = mk_node("ExprYield", 0); } -| YIELD expr { $$ = mk_node("ExprYield", 1, $2); } -| expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); } -| expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } -| expr SHREQ expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); } -| expr MINUSEQ expr { $$ = mk_node("ExprAssignSub", 2, $1, $3); } -| expr ANDEQ expr { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); } -| expr OREQ expr { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); } -| expr PLUSEQ expr { $$ = mk_node("ExprAssignAdd", 2, $1, $3); } -| expr STAREQ expr { $$ = mk_node("ExprAssignMul", 2, $1, $3); } -| expr SLASHEQ expr { $$ = mk_node("ExprAssignDiv", 2, $1, $3); } -| expr CARETEQ expr { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); } -| expr PERCENTEQ expr { $$ = mk_node("ExprAssignRem", 2, $1, $3); } -| expr OROR expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); } -| expr ANDAND expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); } -| expr EQEQ expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); } -| expr NE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); } -| expr '<' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); } -| expr '>' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); } -| expr LE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); } -| expr GE expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); } -| expr '|' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); } -| expr '^' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); } -| expr '&' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); } -| expr SHL expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); } -| expr SHR expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); } -| expr '+' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); } -| expr '-' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); } -| expr '*' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); } -| expr '/' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); } -| expr '%' expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); } -| expr DOTDOT { $$ = mk_node("ExprRange", 2, $1, mk_none()); } -| expr DOTDOT expr { $$ = mk_node("ExprRange", 2, $1, $3); } -| DOTDOT expr { $$ = mk_node("ExprRange", 2, mk_none(), $2); } -| DOTDOT { $$ = mk_node("ExprRange", 2, mk_none(), mk_none()); } -| expr AS ty { $$ = mk_node("ExprCast", 2, $1, $3); } -| expr ':' ty { $$ = mk_node("ExprTypeAscr", 2, $1, $3); } -| BOX expr { $$ = mk_node("ExprBox", 1, $2); } -| expr_qualified_path -| block_expr -| block -| nonblock_prefix_expr -; - -expr_nostruct -: lit { $$ = mk_node("ExprLit", 1, $1); } -| %prec IDENT - path_expr { $$ = mk_node("ExprPath", 1, $1); } -| SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } -| macro_expr { $$ = mk_node("ExprMac", 1, $1); } -| expr_nostruct '?' { $$ = mk_node("ExprTry", 1, $1); } -| expr_nostruct '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } -| expr_nostruct '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } -| expr_nostruct '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } -| expr_nostruct '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 2, $1, $3); } -| '[' vec_expr ']' { $$ = mk_node("ExprVec", 1, $2); } -| '(' maybe_exprs ')' { $$ = mk_node("ExprParen", 1, $2); } -| CONTINUE { $$ = mk_node("ExprAgain", 0); } -| CONTINUE ident { $$ = mk_node("ExprAgain", 1, $2); } -| RETURN { $$ = mk_node("ExprRet", 0); } -| RETURN expr { $$ = mk_node("ExprRet", 1, $2); } -| BREAK { $$ = mk_node("ExprBreak", 0); } -| BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } -| YIELD { $$ = mk_node("ExprYield", 0); } -| YIELD expr { $$ = mk_node("ExprYield", 1, $2); } -| expr_nostruct '=' expr_nostruct { $$ = mk_node("ExprAssign", 2, $1, $3); } -| expr_nostruct SHLEQ expr_nostruct { $$ = mk_node("ExprAssignShl", 2, $1, $3); } -| expr_nostruct SHREQ expr_nostruct { $$ = mk_node("ExprAssignShr", 2, $1, $3); } -| expr_nostruct MINUSEQ expr_nostruct { $$ = mk_node("ExprAssignSub", 2, $1, $3); } -| expr_nostruct ANDEQ expr_nostruct { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); } -| expr_nostruct OREQ expr_nostruct { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); } -| expr_nostruct PLUSEQ expr_nostruct { $$ = mk_node("ExprAssignAdd", 2, $1, $3); } -| expr_nostruct STAREQ expr_nostruct { $$ = mk_node("ExprAssignMul", 2, $1, $3); } -| expr_nostruct SLASHEQ expr_nostruct { $$ = mk_node("ExprAssignDiv", 2, $1, $3); } -| expr_nostruct CARETEQ expr_nostruct { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); } -| expr_nostruct PERCENTEQ expr_nostruct { $$ = mk_node("ExprAssignRem", 2, $1, $3); } -| expr_nostruct OROR expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); } -| expr_nostruct ANDAND expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); } -| expr_nostruct EQEQ expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); } -| expr_nostruct NE expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); } -| expr_nostruct '<' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); } -| expr_nostruct '>' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); } -| expr_nostruct LE expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); } -| expr_nostruct GE expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); } -| expr_nostruct '|' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); } -| expr_nostruct '^' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); } -| expr_nostruct '&' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); } -| expr_nostruct SHL expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); } -| expr_nostruct SHR expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); } -| expr_nostruct '+' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); } -| expr_nostruct '-' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); } -| expr_nostruct '*' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); } -| expr_nostruct '/' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); } -| expr_nostruct '%' expr_nostruct { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); } -| expr_nostruct DOTDOT %prec RANGE { $$ = mk_node("ExprRange", 2, $1, mk_none()); } -| expr_nostruct DOTDOT expr_nostruct { $$ = mk_node("ExprRange", 2, $1, $3); } -| DOTDOT expr_nostruct { $$ = mk_node("ExprRange", 2, mk_none(), $2); } -| DOTDOT { $$ = mk_node("ExprRange", 2, mk_none(), mk_none()); } -| expr_nostruct AS ty { $$ = mk_node("ExprCast", 2, $1, $3); } -| expr_nostruct ':' ty { $$ = mk_node("ExprTypeAscr", 2, $1, $3); } -| BOX expr { $$ = mk_node("ExprBox", 1, $2); } -| expr_qualified_path -| block_expr -| block -| nonblock_prefix_expr_nostruct -; - -nonblock_prefix_expr_nostruct -: '-' expr_nostruct { $$ = mk_node("ExprUnary", 2, mk_atom("UnNeg"), $2); } -| '!' expr_nostruct { $$ = mk_node("ExprUnary", 2, mk_atom("UnNot"), $2); } -| '*' expr_nostruct { $$ = mk_node("ExprUnary", 2, mk_atom("UnDeref"), $2); } -| '&' maybe_mut expr_nostruct { $$ = mk_node("ExprAddrOf", 2, $2, $3); } -| ANDAND maybe_mut expr_nostruct { $$ = mk_node("ExprAddrOf", 1, mk_node("ExprAddrOf", 2, $2, $3)); } -| lambda_expr_nostruct -| MOVE lambda_expr_nostruct { $$ = $2; } -; - -nonblock_prefix_expr -: '-' expr { $$ = mk_node("ExprUnary", 2, mk_atom("UnNeg"), $2); } -| '!' expr { $$ = mk_node("ExprUnary", 2, mk_atom("UnNot"), $2); } -| '*' expr { $$ = mk_node("ExprUnary", 2, mk_atom("UnDeref"), $2); } -| '&' maybe_mut expr { $$ = mk_node("ExprAddrOf", 2, $2, $3); } -| ANDAND maybe_mut expr { $$ = mk_node("ExprAddrOf", 1, mk_node("ExprAddrOf", 2, $2, $3)); } -| lambda_expr -| MOVE lambda_expr { $$ = $2; } -; - -expr_qualified_path -: '<' ty_sum maybe_as_trait_ref '>' MOD_SEP ident maybe_qpath_params -{ - $$ = mk_node("ExprQualifiedPath", 4, $2, $3, $6, $7); -} -| SHL ty_sum maybe_as_trait_ref '>' MOD_SEP ident maybe_as_trait_ref '>' MOD_SEP ident -{ - $$ = mk_node("ExprQualifiedPath", 3, mk_node("ExprQualifiedPath", 3, $2, $3, $6), $7, $10); -} -| SHL ty_sum maybe_as_trait_ref '>' MOD_SEP ident generic_args maybe_as_trait_ref '>' MOD_SEP ident -{ - $$ = mk_node("ExprQualifiedPath", 3, mk_node("ExprQualifiedPath", 4, $2, $3, $6, $7), $8, $11); -} -| SHL ty_sum maybe_as_trait_ref '>' MOD_SEP ident maybe_as_trait_ref '>' MOD_SEP ident generic_args -{ - $$ = mk_node("ExprQualifiedPath", 4, mk_node("ExprQualifiedPath", 3, $2, $3, $6), $7, $10, $11); -} -| SHL ty_sum maybe_as_trait_ref '>' MOD_SEP ident generic_args maybe_as_trait_ref '>' MOD_SEP ident generic_args -{ - $$ = mk_node("ExprQualifiedPath", 4, mk_node("ExprQualifiedPath", 4, $2, $3, $6, $7), $8, $11, $12); -} - -maybe_qpath_params -: MOD_SEP generic_args { $$ = $2; } -| %empty { $$ = mk_none(); } -; - -maybe_as_trait_ref -: AS trait_ref { $$ = $2; } -| %empty { $$ = mk_none(); } -; - -lambda_expr -: %prec LAMBDA - OROR ret_ty expr { $$ = mk_node("ExprFnBlock", 3, mk_none(), $2, $3); } -| %prec LAMBDA - '|' '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, mk_none(), $3, $4); } -| %prec LAMBDA - '|' inferrable_params '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, $2, $4, $5); } -| %prec LAMBDA - '|' inferrable_params OROR lambda_expr_no_first_bar { $$ = mk_node("ExprFnBlock", 3, $2, mk_none(), $4); } -; - -lambda_expr_no_first_bar -: %prec LAMBDA - '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, mk_none(), $2, $3); } -| %prec LAMBDA - inferrable_params '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, $1, $3, $4); } -| %prec LAMBDA - inferrable_params OROR lambda_expr_no_first_bar { $$ = mk_node("ExprFnBlock", 3, $1, mk_none(), $3); } -; - -lambda_expr_nostruct -: %prec LAMBDA - OROR expr_nostruct { $$ = mk_node("ExprFnBlock", 2, mk_none(), $2); } -| %prec LAMBDA - '|' '|' ret_ty expr_nostruct { $$ = mk_node("ExprFnBlock", 3, mk_none(), $3, $4); } -| %prec LAMBDA - '|' inferrable_params '|' expr_nostruct { $$ = mk_node("ExprFnBlock", 2, $2, $4); } -| %prec LAMBDA - '|' inferrable_params OROR lambda_expr_nostruct_no_first_bar { $$ = mk_node("ExprFnBlock", 3, $2, mk_none(), $4); } -; - -lambda_expr_nostruct_no_first_bar -: %prec LAMBDA - '|' ret_ty expr_nostruct { $$ = mk_node("ExprFnBlock", 3, mk_none(), $2, $3); } -| %prec LAMBDA - inferrable_params '|' ret_ty expr_nostruct { $$ = mk_node("ExprFnBlock", 3, $1, $3, $4); } -| %prec LAMBDA - inferrable_params OROR lambda_expr_nostruct_no_first_bar { $$ = mk_node("ExprFnBlock", 3, $1, mk_none(), $3); } -; - -vec_expr -: maybe_exprs -| exprs ';' expr { $$ = mk_node("VecRepeat", 2, $1, $3); } -; - -struct_expr_fields -: field_inits -| field_inits ',' -| maybe_field_inits default_field_init { $$ = ext_node($1, 1, $2); } -| %empty { $$ = mk_none(); } -; - -maybe_field_inits -: field_inits -| field_inits ',' -| %empty { $$ = mk_none(); } -; - -field_inits -: field_init { $$ = mk_node("FieldInits", 1, $1); } -| field_inits ',' field_init { $$ = ext_node($1, 1, $3); } -; - -field_init -: ident { $$ = mk_node("FieldInit", 1, $1); } -| ident ':' expr { $$ = mk_node("FieldInit", 2, $1, $3); } -| LIT_INTEGER ':' expr { $$ = mk_node("FieldInit", 2, mk_atom(yytext), $3); } -; - -default_field_init -: DOTDOT expr { $$ = mk_node("DefaultFieldInit", 1, $2); } -; - -block_expr -: expr_match -| expr_if -| expr_if_let -| expr_while -| expr_while_let -| expr_loop -| expr_for -| UNSAFE block { $$ = mk_node("UnsafeBlock", 1, $2); } -| path_expr '!' maybe_ident braces_delimited_token_trees { $$ = mk_node("Macro", 3, $1, $3, $4); } -; - -full_block_expr -: block_expr -| block_expr_dot -; - -block_expr_dot -: block_expr '.' path_generic_args_with_colons %prec IDENT { $$ = mk_node("ExprField", 2, $1, $3); } -| block_expr_dot '.' path_generic_args_with_colons %prec IDENT { $$ = mk_node("ExprField", 2, $1, $3); } -| block_expr '.' path_generic_args_with_colons '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 3, $1, $3, $5); } -| block_expr_dot '.' path_generic_args_with_colons '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 3, $1, $3, $5); } -| block_expr '.' path_generic_args_with_colons '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 3, $1, $3, $5); } -| block_expr_dot '.' path_generic_args_with_colons '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 3, $1, $3, $5); } -| block_expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } -| block_expr_dot '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } -; - -expr_match -: MATCH expr_nostruct '{' '}' { $$ = mk_node("ExprMatch", 1, $2); } -| MATCH expr_nostruct '{' match_clauses '}' { $$ = mk_node("ExprMatch", 2, $2, $4); } -| MATCH expr_nostruct '{' match_clauses nonblock_match_clause '}' { $$ = mk_node("ExprMatch", 2, $2, ext_node($4, 1, $5)); } -| MATCH expr_nostruct '{' nonblock_match_clause '}' { $$ = mk_node("ExprMatch", 2, $2, mk_node("Arms", 1, $4)); } -; - -match_clauses -: match_clause { $$ = mk_node("Arms", 1, $1); } -| match_clauses match_clause { $$ = ext_node($1, 1, $2); } -; - -match_clause -: nonblock_match_clause ',' -| block_match_clause -| block_match_clause ',' -; - -nonblock_match_clause -: maybe_outer_attrs pats_or maybe_guard FAT_ARROW nonblock_expr { $$ = mk_node("ArmNonblock", 4, $1, $2, $3, $5); } -| maybe_outer_attrs pats_or maybe_guard FAT_ARROW block_expr_dot { $$ = mk_node("ArmNonblock", 4, $1, $2, $3, $5); } -; - -block_match_clause -: maybe_outer_attrs pats_or maybe_guard FAT_ARROW block { $$ = mk_node("ArmBlock", 4, $1, $2, $3, $5); } -| maybe_outer_attrs pats_or maybe_guard FAT_ARROW block_expr { $$ = mk_node("ArmBlock", 4, $1, $2, $3, $5); } -; - -maybe_guard -: IF expr_nostruct { $$ = $2; } -| %empty { $$ = mk_none(); } -; - -expr_if -: IF expr_nostruct block { $$ = mk_node("ExprIf", 2, $2, $3); } -| IF expr_nostruct block ELSE block_or_if { $$ = mk_node("ExprIf", 3, $2, $3, $5); } -; - -expr_if_let -: IF LET pat '=' expr_nostruct block { $$ = mk_node("ExprIfLet", 3, $3, $5, $6); } -| IF LET pat '=' expr_nostruct block ELSE block_or_if { $$ = mk_node("ExprIfLet", 4, $3, $5, $6, $8); } -; - -block_or_if -: block -| expr_if -| expr_if_let -; - -expr_while -: maybe_label WHILE expr_nostruct block { $$ = mk_node("ExprWhile", 3, $1, $3, $4); } -; - -expr_while_let -: maybe_label WHILE LET pat '=' expr_nostruct block { $$ = mk_node("ExprWhileLet", 4, $1, $4, $6, $7); } -; - -expr_loop -: maybe_label LOOP block { $$ = mk_node("ExprLoop", 2, $1, $3); } -; - -expr_for -: maybe_label FOR pat IN expr_nostruct block { $$ = mk_node("ExprForLoop", 4, $1, $3, $5, $6); } -; - -maybe_label -: lifetime ':' -| %empty { $$ = mk_none(); } -; - -let -: LET pat maybe_ty_ascription maybe_init_expr ';' { $$ = mk_node("DeclLocal", 3, $2, $3, $4); } -; - -//////////////////////////////////////////////////////////////////////// -// Part 5: Macros and misc. rules -//////////////////////////////////////////////////////////////////////// - -lit -: LIT_BYTE { $$ = mk_node("LitByte", 1, mk_atom(yytext)); } -| LIT_CHAR { $$ = mk_node("LitChar", 1, mk_atom(yytext)); } -| LIT_INTEGER { $$ = mk_node("LitInteger", 1, mk_atom(yytext)); } -| LIT_FLOAT { $$ = mk_node("LitFloat", 1, mk_atom(yytext)); } -| TRUE { $$ = mk_node("LitBool", 1, mk_atom(yytext)); } -| FALSE { $$ = mk_node("LitBool", 1, mk_atom(yytext)); } -| str -; - -str -: LIT_STR { $$ = mk_node("LitStr", 1, mk_atom(yytext), mk_atom("CookedStr")); } -| LIT_STR_RAW { $$ = mk_node("LitStr", 1, mk_atom(yytext), mk_atom("RawStr")); } -| LIT_BYTE_STR { $$ = mk_node("LitByteStr", 1, mk_atom(yytext), mk_atom("ByteStr")); } -| LIT_BYTE_STR_RAW { $$ = mk_node("LitByteStr", 1, mk_atom(yytext), mk_atom("RawByteStr")); } -; - -maybe_ident -: %empty { $$ = mk_none(); } -| ident -; - -ident -: IDENT { $$ = mk_node("ident", 1, mk_atom(yytext)); } -// Weak keywords that can be used as identifiers -| CATCH { $$ = mk_node("ident", 1, mk_atom(yytext)); } -| DEFAULT { $$ = mk_node("ident", 1, mk_atom(yytext)); } -| UNION { $$ = mk_node("ident", 1, mk_atom(yytext)); } -; - -unpaired_token -: SHL { $$ = mk_atom(yytext); } -| SHR { $$ = mk_atom(yytext); } -| LE { $$ = mk_atom(yytext); } -| EQEQ { $$ = mk_atom(yytext); } -| NE { $$ = mk_atom(yytext); } -| GE { $$ = mk_atom(yytext); } -| ANDAND { $$ = mk_atom(yytext); } -| OROR { $$ = mk_atom(yytext); } -| LARROW { $$ = mk_atom(yytext); } -| SHLEQ { $$ = mk_atom(yytext); } -| SHREQ { $$ = mk_atom(yytext); } -| MINUSEQ { $$ = mk_atom(yytext); } -| ANDEQ { $$ = mk_atom(yytext); } -| OREQ { $$ = mk_atom(yytext); } -| PLUSEQ { $$ = mk_atom(yytext); } -| STAREQ { $$ = mk_atom(yytext); } -| SLASHEQ { $$ = mk_atom(yytext); } -| CARETEQ { $$ = mk_atom(yytext); } -| PERCENTEQ { $$ = mk_atom(yytext); } -| DOTDOT { $$ = mk_atom(yytext); } -| DOTDOTDOT { $$ = mk_atom(yytext); } -| MOD_SEP { $$ = mk_atom(yytext); } -| RARROW { $$ = mk_atom(yytext); } -| FAT_ARROW { $$ = mk_atom(yytext); } -| LIT_BYTE { $$ = mk_atom(yytext); } -| LIT_CHAR { $$ = mk_atom(yytext); } -| LIT_INTEGER { $$ = mk_atom(yytext); } -| LIT_FLOAT { $$ = mk_atom(yytext); } -| LIT_STR { $$ = mk_atom(yytext); } -| LIT_STR_RAW { $$ = mk_atom(yytext); } -| LIT_BYTE_STR { $$ = mk_atom(yytext); } -| LIT_BYTE_STR_RAW { $$ = mk_atom(yytext); } -| IDENT { $$ = mk_atom(yytext); } -| UNDERSCORE { $$ = mk_atom(yytext); } -| LIFETIME { $$ = mk_atom(yytext); } -| SELF { $$ = mk_atom(yytext); } -| STATIC { $$ = mk_atom(yytext); } -| ABSTRACT { $$ = mk_atom(yytext); } -| ALIGNOF { $$ = mk_atom(yytext); } -| AS { $$ = mk_atom(yytext); } -| BECOME { $$ = mk_atom(yytext); } -| BREAK { $$ = mk_atom(yytext); } -| CATCH { $$ = mk_atom(yytext); } -| CRATE { $$ = mk_atom(yytext); } -| DEFAULT { $$ = mk_atom(yytext); } -| DO { $$ = mk_atom(yytext); } -| ELSE { $$ = mk_atom(yytext); } -| ENUM { $$ = mk_atom(yytext); } -| EXTERN { $$ = mk_atom(yytext); } -| FALSE { $$ = mk_atom(yytext); } -| FINAL { $$ = mk_atom(yytext); } -| FN { $$ = mk_atom(yytext); } -| FOR { $$ = mk_atom(yytext); } -| IF { $$ = mk_atom(yytext); } -| IMPL { $$ = mk_atom(yytext); } -| IN { $$ = mk_atom(yytext); } -| LET { $$ = mk_atom(yytext); } -| LOOP { $$ = mk_atom(yytext); } -| MACRO { $$ = mk_atom(yytext); } -| MATCH { $$ = mk_atom(yytext); } -| MOD { $$ = mk_atom(yytext); } -| MOVE { $$ = mk_atom(yytext); } -| MUT { $$ = mk_atom(yytext); } -| OFFSETOF { $$ = mk_atom(yytext); } -| OVERRIDE { $$ = mk_atom(yytext); } -| PRIV { $$ = mk_atom(yytext); } -| PUB { $$ = mk_atom(yytext); } -| PURE { $$ = mk_atom(yytext); } -| REF { $$ = mk_atom(yytext); } -| RETURN { $$ = mk_atom(yytext); } -| STRUCT { $$ = mk_atom(yytext); } -| SIZEOF { $$ = mk_atom(yytext); } -| SUPER { $$ = mk_atom(yytext); } -| TRUE { $$ = mk_atom(yytext); } -| TRAIT { $$ = mk_atom(yytext); } -| TYPE { $$ = mk_atom(yytext); } -| UNION { $$ = mk_atom(yytext); } -| UNSAFE { $$ = mk_atom(yytext); } -| UNSIZED { $$ = mk_atom(yytext); } -| USE { $$ = mk_atom(yytext); } -| VIRTUAL { $$ = mk_atom(yytext); } -| WHILE { $$ = mk_atom(yytext); } -| YIELD { $$ = mk_atom(yytext); } -| CONTINUE { $$ = mk_atom(yytext); } -| PROC { $$ = mk_atom(yytext); } -| BOX { $$ = mk_atom(yytext); } -| CONST { $$ = mk_atom(yytext); } -| WHERE { $$ = mk_atom(yytext); } -| TYPEOF { $$ = mk_atom(yytext); } -| INNER_DOC_COMMENT { $$ = mk_atom(yytext); } -| OUTER_DOC_COMMENT { $$ = mk_atom(yytext); } -| SHEBANG { $$ = mk_atom(yytext); } -| STATIC_LIFETIME { $$ = mk_atom(yytext); } -| ';' { $$ = mk_atom(yytext); } -| ',' { $$ = mk_atom(yytext); } -| '.' { $$ = mk_atom(yytext); } -| '@' { $$ = mk_atom(yytext); } -| '#' { $$ = mk_atom(yytext); } -| '~' { $$ = mk_atom(yytext); } -| ':' { $$ = mk_atom(yytext); } -| '$' { $$ = mk_atom(yytext); } -| '=' { $$ = mk_atom(yytext); } -| '?' { $$ = mk_atom(yytext); } -| '!' { $$ = mk_atom(yytext); } -| '<' { $$ = mk_atom(yytext); } -| '>' { $$ = mk_atom(yytext); } -| '-' { $$ = mk_atom(yytext); } -| '&' { $$ = mk_atom(yytext); } -| '|' { $$ = mk_atom(yytext); } -| '+' { $$ = mk_atom(yytext); } -| '*' { $$ = mk_atom(yytext); } -| '/' { $$ = mk_atom(yytext); } -| '^' { $$ = mk_atom(yytext); } -| '%' { $$ = mk_atom(yytext); } -; - -token_trees -: %empty { $$ = mk_node("TokenTrees", 0); } -| token_trees token_tree { $$ = ext_node($1, 1, $2); } -; - -token_tree -: delimited_token_trees -| unpaired_token { $$ = mk_node("TTTok", 1, $1); } -; - -delimited_token_trees -: parens_delimited_token_trees -| braces_delimited_token_trees -| brackets_delimited_token_trees -; - -parens_delimited_token_trees -: '(' token_trees ')' -{ - $$ = mk_node("TTDelim", 3, - mk_node("TTTok", 1, mk_atom("(")), - $2, - mk_node("TTTok", 1, mk_atom(")"))); -} -; - -braces_delimited_token_trees -: '{' token_trees '}' -{ - $$ = mk_node("TTDelim", 3, - mk_node("TTTok", 1, mk_atom("{")), - $2, - mk_node("TTTok", 1, mk_atom("}"))); -} -; - -brackets_delimited_token_trees -: '[' token_trees ']' -{ - $$ = mk_node("TTDelim", 3, - mk_node("TTTok", 1, mk_atom("[")), - $2, - mk_node("TTTok", 1, mk_atom("]"))); -} -;
diff --git a/src/grammar/raw-string-literal-ambiguity.md b/src/grammar/raw-string-literal-ambiguity.md deleted file mode 100644 index c909f23..0000000 --- a/src/grammar/raw-string-literal-ambiguity.md +++ /dev/null
@@ -1,64 +0,0 @@ -Rust's lexical grammar is not context-free. Raw string literals are the source -of the problem. Informally, a raw string literal is an `r`, followed by `N` -hashes (where N can be zero), a quote, any characters, then a quote followed -by `N` hashes. Critically, once inside the first pair of quotes, -another quote cannot be followed by `N` consecutive hashes. e.g. -`r###""###"###` is invalid. - -This grammar describes this as best possible: - - R -> 'r' S - S -> '"' B '"' - S -> '#' S '#' - B -> . B - B -> ε - -Where `.` represents any character, and `ε` the empty string. Consider the -string `r#""#"#`. This string is not a valid raw string literal, but can be -accepted as one by the above grammar, using the derivation: - - R : #""#"# - S : ""#" - S : "# - B : # - B : ε - -(Where `T : U` means the rule `T` is applied, and `U` is the remainder of the -string.) The difficulty arises from the fact that it is fundamentally -context-sensitive. In particular, the context needed is the number of hashes. - -To prove that Rust's string literals are not context-free, we will use -the fact that context-free languages are closed under intersection with -regular languages, and the -[pumping lemma for context-free languages](https://en.wikipedia.org/wiki/Pumping_lemma_for_context-free_languages). - -Consider the regular language `R = r#+""#*"#+`. If Rust's raw string literals are -context-free, then their intersection with `R`, `R'`, should also be context-free. -Therefore, to prove that raw string literals are not context-free, -it is sufficient to prove that `R'` is not context-free. - -The language `R'` is `{r#^n""#^m"#^n | m < n}`. - -Assume `R'` *is* context-free. Then `R'` has some pumping length `p > 0` for which -the pumping lemma applies. Consider the following string `s` in `R'`: - -`r#^p""#^{p-1}"#^p` - -e.g. for `p = 2`: `s = r##""#"##` - -Then `s = uvwxy` for some choice of `uvwxy` such that `vx` is non-empty, -`|vwx| < p+1`, and `uv^iwx^iy` is in `R'` for all `i >= 0`. - -Neither `v` nor `x` can contain a `"` or `r`, as the number of these characters -in any string in `R'` is fixed. So `v` and `x` contain only hashes. -Consequently, of the three sequences of hashes, `v` and `x` combined -can only pump two of them. -If we ever choose the central sequence of hashes, then one of the outer sequences -will not grow when we pump, leading to an imbalance between the outer sequences. -Therefore, we must pump both outer sequences of hashes. However, -there are `p+2` characters between these two sequences of hashes, and `|vwx|` must -be less than `p+1`. Therefore we have a contradiction, and `R'` must not be -context-free. - -Since `R'` is not context-free, it follows that the Rust's raw string literals -must not be context-free.
diff --git a/src/grammar/testparser.py b/src/grammar/testparser.py deleted file mode 100755 index 4b5a7fb..0000000 --- a/src/grammar/testparser.py +++ /dev/null
@@ -1,66 +0,0 @@ -#!/usr/bin/env python - -# ignore-tidy-linelength - -import sys - -import os -import subprocess -import argparse - -# usage: testparser.py [-h] [-p PARSER [PARSER ...]] -s SOURCE_DIR - -# Parsers should read from stdin and return exit status 0 for a -# successful parse, and nonzero for an unsuccessful parse - -parser = argparse.ArgumentParser() -parser.add_argument('-p', '--parser', nargs='+') -parser.add_argument('-s', '--source-dir', nargs=1, required=True) -args = parser.parse_args(sys.argv[1:]) - -total = 0 -ok = {} -bad = {} -for parser in args.parser: - ok[parser] = 0 - bad[parser] = [] -devnull = open(os.devnull, 'w') -print("\n") - -for base, dirs, files in os.walk(args.source_dir[0]): - for f in filter(lambda p: p.endswith('.rs'), files): - p = os.path.join(base, f) - parse_fail = 'parse-fail' in p - if sys.version_info.major == 3: - lines = open(p, encoding='utf-8').readlines() - else: - lines = open(p).readlines() - if any('ignore-test' in line or 'ignore-lexer-test' in line for line in lines): - continue - total += 1 - for parser in args.parser: - if subprocess.call(parser, stdin=open(p), stderr=subprocess.STDOUT, stdout=devnull) == 0: - if parse_fail: - bad[parser].append(p) - else: - ok[parser] += 1 - else: - if parse_fail: - ok[parser] += 1 - else: - bad[parser].append(p) - parser_stats = ', '.join(['{}: {}'.format(parser, ok[parser]) for parser in args.parser]) - sys.stdout.write("\033[K\r total: {}, {}, scanned {}" - .format(total, os.path.relpath(parser_stats), os.path.relpath(p))) - -devnull.close() - -print("\n") - -for parser in args.parser: - filename = os.path.basename(parser) + '.bad' - print("writing {} files that did not yield the correct result with {} to {}".format(len(bad[parser]), parser, filename)) - with open(filename, "w") as f: - for p in bad[parser]: - f.write(p) - f.write("\n")
diff --git a/src/grammar/tokens.h b/src/grammar/tokens.h deleted file mode 100644 index 297e3dc..0000000 --- a/src/grammar/tokens.h +++ /dev/null
@@ -1,99 +0,0 @@ -enum Token { - SHL = 257, // Parser generators reserve 0-256 for char literals - SHR, - LE, - EQEQ, - NE, - GE, - ANDAND, - OROR, - SHLEQ, - SHREQ, - MINUSEQ, - ANDEQ, - OREQ, - PLUSEQ, - STAREQ, - SLASHEQ, - CARETEQ, - PERCENTEQ, - DOTDOT, - DOTDOTDOT, - MOD_SEP, - LARROW, - RARROW, - FAT_ARROW, - LIT_BYTE, - LIT_CHAR, - LIT_INTEGER, - LIT_FLOAT, - LIT_STR, - LIT_STR_RAW, - LIT_BYTE_STR, - LIT_BYTE_STR_RAW, - IDENT, - UNDERSCORE, - LIFETIME, - - // keywords - SELF, - STATIC, - ABSTRACT, - ALIGNOF, - AS, - BECOME, - BREAK, - CATCH, - CRATE, - DEFAULT, - DO, - ELSE, - ENUM, - EXTERN, - FALSE, - FINAL, - FN, - FOR, - IF, - IMPL, - IN, - LET, - LOOP, - MACRO, - MATCH, - MOD, - MOVE, - MUT, - OFFSETOF, - OVERRIDE, - PRIV, - PUB, - PURE, - REF, - RETURN, - SIZEOF, - STRUCT, - SUPER, - UNION, - TRUE, - TRAIT, - TYPE, - UNSAFE, - UNSIZED, - USE, - VIRTUAL, - WHILE, - YIELD, - CONTINUE, - PROC, - BOX, - CONST, - WHERE, - TYPEOF, - INNER_DOC_COMMENT, - OUTER_DOC_COMMENT, - - SHEBANG, - SHEBANG_LINE, - STATIC_LIFETIME -};
diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index a39fcd5..9bc76f5 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs
@@ -240,7 +240,6 @@ #[stable(feature = "global_alloc", since = "1.28.0")] #[rustc_allocator_nounwind] pub fn handle_alloc_error(layout: Layout) -> ! { - #[cfg_attr(bootstrap, allow(improper_ctypes))] extern "Rust" { #[lang = "oom"] fn oom_impl(layout: Layout) -> !;
diff --git a/src/liballoc/borrow.rs b/src/liballoc/borrow.rs index a9c5bce..d2bdda8 100644 --- a/src/liballoc/borrow.rs +++ b/src/liballoc/borrow.rs
@@ -207,6 +207,47 @@ } impl<B: ?Sized + ToOwned> Cow<'_, B> { + /// Returns true if the data is borrowed, i.e. if `to_mut` would require additional work. + /// + /// # Examples + /// + /// ``` + /// #![feature(cow_is_borrowed)] + /// use std::borrow::Cow; + /// + /// let cow = Cow::Borrowed("moo"); + /// assert!(cow.is_borrowed()); + /// + /// let bull: Cow<'_, str> = Cow::Owned("...moo?".to_string()); + /// assert!(!bull.is_borrowed()); + /// ``` + #[unstable(feature = "cow_is_borrowed", issue = "65143")] + pub fn is_borrowed(&self) -> bool { + match *self { + Borrowed(_) => true, + Owned(_) => false, + } + } + + /// Returns true if the data is owned, i.e. if `to_mut` would be a no-op. + /// + /// # Examples + /// + /// ``` + /// #![feature(cow_is_borrowed)] + /// use std::borrow::Cow; + /// + /// let cow: Cow<'_, str> = Cow::Owned("moo".to_string()); + /// assert!(cow.is_owned()); + /// + /// let bull = Cow::Borrowed("...moo?"); + /// assert!(!bull.is_owned()); + /// ``` + #[unstable(feature = "cow_is_borrowed", issue = "65143")] + pub fn is_owned(&self) -> bool { + !self.is_borrowed() + } + /// Acquires a mutable reference to the owned form of the data. /// /// Clones the data if it is not already owned.
diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index c61e318..567b8ea 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs
@@ -29,10 +29,8 @@ //! Nil, //! } //! -//! fn main() { -//! let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil)))); -//! println!("{:?}", list); -//! } +//! let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil)))); +//! println!("{:?}", list); //! ``` //! //! This will print `Cons(1, Cons(2, Nil))`. @@ -144,6 +142,9 @@ #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_uninit() -> Box<mem::MaybeUninit<T>> { let layout = alloc::Layout::new::<mem::MaybeUninit<T>>(); + if layout.size() == 0 { + return Box(NonNull::dangling().into()) + } let ptr = unsafe { Global.alloc(layout) .unwrap_or_else(|_| alloc::handle_alloc_error(layout)) @@ -184,9 +185,16 @@ #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_uninit_slice(len: usize) -> Box<[mem::MaybeUninit<T>]> { let layout = alloc::Layout::array::<mem::MaybeUninit<T>>(len).unwrap(); - let ptr = unsafe { alloc::alloc(layout) }; - let unique = Unique::new(ptr).unwrap_or_else(|| alloc::handle_alloc_error(layout)); - let slice = unsafe { slice::from_raw_parts_mut(unique.cast().as_ptr(), len) }; + let ptr = if layout.size() == 0 { + NonNull::dangling() + } else { + unsafe { + Global.alloc(layout) + .unwrap_or_else(|_| alloc::handle_alloc_error(layout)) + .cast() + } + }; + let slice = unsafe { slice::from_raw_parts_mut(ptr.as_ptr(), len) }; Box(Unique::from(slice)) } } @@ -375,14 +383,12 @@ /// ``` /// #![feature(box_into_raw_non_null)] /// - /// fn main() { - /// let x = Box::new(5); - /// let ptr = Box::into_raw_non_null(x); + /// let x = Box::new(5); + /// let ptr = Box::into_raw_non_null(x); /// - /// // Clean up the memory by converting the NonNull pointer back - /// // into a Box and letting the Box be dropped. - /// let x = unsafe { Box::from_raw(ptr.as_ptr()) }; - /// } + /// // Clean up the memory by converting the NonNull pointer back + /// // into a Box and letting the Box be dropped. + /// let x = unsafe { Box::from_raw(ptr.as_ptr()) }; /// ``` #[unstable(feature = "box_into_raw_non_null", issue = "47336")] #[inline] @@ -428,23 +434,19 @@ /// Simple usage: /// /// ``` - /// fn main() { - /// let x = Box::new(41); - /// let static_ref: &'static mut usize = Box::leak(x); - /// *static_ref += 1; - /// assert_eq!(*static_ref, 42); - /// } + /// let x = Box::new(41); + /// let static_ref: &'static mut usize = Box::leak(x); + /// *static_ref += 1; + /// assert_eq!(*static_ref, 42); /// ``` /// /// Unsized data: /// /// ``` - /// fn main() { - /// let x = vec![1, 2, 3].into_boxed_slice(); - /// let static_ref = Box::leak(x); - /// static_ref[0] = 4; - /// assert_eq!(*static_ref, [4, 2, 3]); - /// } + /// let x = vec![1, 2, 3].into_boxed_slice(); + /// let static_ref = Box::leak(x); + /// static_ref[0] = 4; + /// assert_eq!(*static_ref, [4, 2, 3]); /// ``` #[stable(feature = "box_leak", since = "1.26.0")] #[inline] @@ -780,11 +782,9 @@ /// } /// } /// - /// fn main() { - /// let my_string = "Hello World".to_string(); - /// print_if_string(Box::new(my_string)); - /// print_if_string(Box::new(0i8)); - /// } + /// let my_string = "Hello World".to_string(); + /// print_if_string(Box::new(my_string)); + /// print_if_string(Box::new(0i8)); /// ``` pub fn downcast<T: Any>(self) -> Result<Box<T>, Box<dyn Any>> { if self.is::<T>() { @@ -814,11 +814,9 @@ /// } /// } /// - /// fn main() { - /// let my_string = "Hello World".to_string(); - /// print_if_string(Box::new(my_string)); - /// print_if_string(Box::new(0i8)); - /// } + /// let my_string = "Hello World".to_string(); + /// print_if_string(Box::new(my_string)); + /// print_if_string(Box::new(0i8)); /// ``` pub fn downcast<T: Any>(self) -> Result<Box<T>, Box<dyn Any + Send>> { <Box<dyn Any>>::downcast(self).map_err(|s| unsafe { @@ -883,11 +881,33 @@ fn nth(&mut self, n: usize) -> Option<I::Item> { (**self).nth(n) } + fn last(self) -> Option<I::Item> { + BoxIter::last(self) + } } +trait BoxIter { + type Item; + fn last(self) -> Option<Self::Item>; +} + +impl<I: Iterator + ?Sized> BoxIter for Box<I> { + type Item = I::Item; + default fn last(self) -> Option<I::Item> { + #[inline] + fn some<T>(_: Option<T>, x: T) -> Option<T> { + Some(x) + } + + self.fold(None, some) + } +} + +/// Specialization for sized `I`s that uses `I`s implementation of `last()` +/// instead of the default. #[stable(feature = "rust1", since = "1.0.0")] -impl<I: Iterator + Sized> Iterator for Box<I> { - fn last(self) -> Option<I::Item> where I: Sized { +impl<I: Iterator> BoxIter for Box<I> { + fn last(self) -> Option<I::Item> { (*self).last() } }
diff --git a/src/liballoc/collections/binary_heap.rs b/src/liballoc/collections/binary_heap.rs index 3d04f30..fda6f09 100644 --- a/src/liballoc/collections/binary_heap.rs +++ b/src/liballoc/collections/binary_heap.rs
@@ -146,7 +146,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use core::ops::{Deref, DerefMut}; -use core::iter::{FromIterator, FusedIterator}; +use core::iter::{FromIterator, FusedIterator, TrustedLen}; use core::mem::{swap, size_of, ManuallyDrop}; use core::ptr; use core::fmt; @@ -648,6 +648,36 @@ self.extend(other.drain()); } } + + /// Returns an iterator which retrieves elements in heap order. + /// The retrieved elements are removed from the original heap. + /// The remaining elements will be removed on drop in heap order. + /// + /// Note: + /// * `.drain_sorted()` is O(n lg n); much slower than `.drain()`. + /// You should use the latter for most cases. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(binary_heap_drain_sorted)] + /// use std::collections::BinaryHeap; + /// + /// let mut heap = BinaryHeap::from(vec![1, 2, 3, 4, 5]); + /// assert_eq!(heap.len(), 5); + /// + /// drop(heap.drain_sorted()); // removes all elements in heap order + /// assert_eq!(heap.len(), 0); + /// ``` + #[inline] + #[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] + pub fn drain_sorted(&mut self) -> DrainSorted<'_, T> { + DrainSorted { + inner: self, + } + } } impl<T> BinaryHeap<T> { @@ -672,6 +702,27 @@ Iter { iter: self.data.iter() } } + /// Returns an iterator which retrieves elements in heap order. + /// This method consumes the original heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(binary_heap_into_iter_sorted)] + /// use std::collections::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5]); + /// + /// assert_eq!(heap.into_iter_sorted().take(2).collect::<Vec<_>>(), vec![5, 4]); + /// ``` + #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] + pub fn into_iter_sorted(self) -> IntoIterSorted<T> { + IntoIterSorted { + inner: self, + } + } + /// Returns the greatest item in the binary heap, or `None` if it is empty. /// /// # Examples @@ -1115,6 +1166,37 @@ #[stable(feature = "fused", since = "1.26.0")] impl<T> FusedIterator for IntoIter<T> {} +#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] +#[derive(Clone, Debug)] +pub struct IntoIterSorted<T> { + inner: BinaryHeap<T>, +} + +#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] +impl<T: Ord> Iterator for IntoIterSorted<T> { + type Item = T; + + #[inline] + fn next(&mut self) -> Option<T> { + self.inner.pop() + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + let exact = self.inner.len(); + (exact, Some(exact)) + } +} + +#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] +impl<T: Ord> ExactSizeIterator for IntoIterSorted<T> {} + +#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] +impl<T: Ord> FusedIterator for IntoIterSorted<T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl<T: Ord> TrustedLen for IntoIterSorted<T> {} + /// A draining iterator over the elements of a `BinaryHeap`. /// /// This `struct` is created by the [`drain`] method on [`BinaryHeap`]. See its @@ -1161,6 +1243,52 @@ #[stable(feature = "fused", since = "1.26.0")] impl<T> FusedIterator for Drain<'_, T> {} +/// A draining iterator over the elements of a `BinaryHeap`. +/// +/// This `struct` is created by the [`drain_sorted`] method on [`BinaryHeap`]. See its +/// documentation for more. +/// +/// [`drain_sorted`]: struct.BinaryHeap.html#method.drain_sorted +/// [`BinaryHeap`]: struct.BinaryHeap.html +#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] +#[derive(Debug)] +pub struct DrainSorted<'a, T: Ord> { + inner: &'a mut BinaryHeap<T>, +} + +#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] +impl<'a, T: Ord> Drop for DrainSorted<'a, T> { + /// Removes heap elements in heap order. + fn drop(&mut self) { + while let Some(_) = self.inner.pop() {} + } +} + +#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] +impl<T: Ord> Iterator for DrainSorted<'_, T> { + type Item = T; + + #[inline] + fn next(&mut self) -> Option<T> { + self.inner.pop() + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + let exact = self.inner.len(); + (exact, Some(exact)) + } +} + +#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] +impl<T: Ord> ExactSizeIterator for DrainSorted<'_, T> { } + +#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] +impl<T: Ord> FusedIterator for DrainSorted<'_, T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl<T: Ord> TrustedLen for DrainSorted<'_, T> {} + #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] impl<T: Ord> From<Vec<T>> for BinaryHeap<T> { /// Converts a `Vec<T>` into a `BinaryHeap<T>`.
diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs index 1683b81..83fd448 100644 --- a/src/liballoc/collections/btree/map.rs +++ b/src/liballoc/collections/btree/map.rs
@@ -580,7 +580,6 @@ /// # Examples /// /// ``` - /// #![feature(map_get_key_value)] /// use std::collections::BTreeMap; /// /// let mut map = BTreeMap::new(); @@ -588,7 +587,7 @@ /// assert_eq!(map.get_key_value(&1), Some((&1, &"a"))); /// assert_eq!(map.get_key_value(&2), None); /// ``` - #[unstable(feature = "map_get_key_value", issue = "49347")] + #[stable(feature = "map_get_key_value", since = "1.40.0")] pub fn get_key_value<Q: ?Sized>(&self, k: &Q) -> Option<(&K, &V)> where K: Borrow<Q>, Q: Ord @@ -2227,14 +2226,12 @@ /// # Examples /// /// ``` - /// # fn main() { /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap<&str, Option<usize>> = BTreeMap::new(); /// map.entry("poneyland").or_default(); /// /// assert_eq!(map["poneyland"], None); - /// # } /// ``` pub fn or_default(self) -> &'a mut V { match self {
diff --git a/src/liballoc/collections/btree/set.rs b/src/liballoc/collections/btree/set.rs index 0cb91ba..f079635 100644 --- a/src/liballoc/collections/btree/set.rs +++ b/src/liballoc/collections/btree/set.rs
@@ -2,7 +2,7 @@ // to TreeMap use core::borrow::Borrow; -use core::cmp::Ordering::{self, Less, Greater, Equal}; +use core::cmp::Ordering::{Less, Greater, Equal}; use core::cmp::{max, min}; use core::fmt::{self, Debug}; use core::iter::{Peekable, FromIterator, FusedIterator}; @@ -109,6 +109,77 @@ iter: btree_map::Range<'a, T, ()>, } +/// Core of SymmetricDifference and Union. +/// More efficient than btree.map.MergeIter, +/// and crucially for SymmetricDifference, nexts() reports on both sides. +#[derive(Clone)] +struct MergeIterInner<I> + where I: Iterator, + I::Item: Copy, +{ + a: I, + b: I, + peeked: Option<MergeIterPeeked<I>>, +} + +#[derive(Copy, Clone, Debug)] +enum MergeIterPeeked<I: Iterator> { + A(I::Item), + B(I::Item), +} + +impl<I> MergeIterInner<I> + where I: ExactSizeIterator + FusedIterator, + I::Item: Copy + Ord, +{ + fn new(a: I, b: I) -> Self { + MergeIterInner { a, b, peeked: None } + } + + fn nexts(&mut self) -> (Option<I::Item>, Option<I::Item>) { + let mut a_next = match self.peeked { + Some(MergeIterPeeked::A(next)) => Some(next), + _ => self.a.next(), + }; + let mut b_next = match self.peeked { + Some(MergeIterPeeked::B(next)) => Some(next), + _ => self.b.next(), + }; + let ord = match (a_next, b_next) { + (None, None) => Equal, + (_, None) => Less, + (None, _) => Greater, + (Some(a1), Some(b1)) => a1.cmp(&b1), + }; + self.peeked = match ord { + Less => b_next.take().map(MergeIterPeeked::B), + Equal => None, + Greater => a_next.take().map(MergeIterPeeked::A), + }; + (a_next, b_next) + } + + fn lens(&self) -> (usize, usize) { + match self.peeked { + Some(MergeIterPeeked::A(_)) => (1 + self.a.len(), self.b.len()), + Some(MergeIterPeeked::B(_)) => (self.a.len(), 1 + self.b.len()), + _ => (self.a.len(), self.b.len()), + } + } +} + +impl<I> Debug for MergeIterInner<I> + where I: Iterator + Debug, + I::Item: Copy + Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("MergeIterInner") + .field(&self.a) + .field(&self.b) + .finish() + } +} + /// A lazy iterator producing elements in the difference of `BTreeSet`s. /// /// This `struct` is created by the [`difference`] method on [`BTreeSet`]. @@ -120,34 +191,25 @@ pub struct Difference<'a, T: 'a> { inner: DifferenceInner<'a, T>, } +#[derive(Debug)] enum DifferenceInner<'a, T: 'a> { Stitch { + // iterate all of self and some of other, spotting matches along the way self_iter: Iter<'a, T>, other_iter: Peekable<Iter<'a, T>>, }, Search { + // iterate a small set, look up in the large set self_iter: Iter<'a, T>, other_set: &'a BTreeSet<T>, }, + Iterate(Iter<'a, T>), // simply stream self's elements } #[stable(feature = "collection_debug", since = "1.17.0")] impl<T: fmt::Debug> fmt::Debug for Difference<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.inner { - DifferenceInner::Stitch { - self_iter, - other_iter, - } => f - .debug_tuple("Difference") - .field(&self_iter) - .field(&other_iter) - .finish(), - DifferenceInner::Search { - self_iter, - other_set: _, - } => f.debug_tuple("Difference").field(&self_iter).finish(), - } + f.debug_tuple("Difference").field(&self.inner).finish() } } @@ -159,18 +221,12 @@ /// [`BTreeSet`]: struct.BTreeSet.html /// [`symmetric_difference`]: struct.BTreeSet.html#method.symmetric_difference #[stable(feature = "rust1", since = "1.0.0")] -pub struct SymmetricDifference<'a, T: 'a> { - a: Peekable<Iter<'a, T>>, - b: Peekable<Iter<'a, T>>, -} +pub struct SymmetricDifference<'a, T: 'a>(MergeIterInner<Iter<'a, T>>); #[stable(feature = "collection_debug", since = "1.17.0")] impl<T: fmt::Debug> fmt::Debug for SymmetricDifference<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("SymmetricDifference") - .field(&self.a) - .field(&self.b) - .finish() + f.debug_tuple("SymmetricDifference").field(&self.0).finish() } } @@ -185,34 +241,25 @@ pub struct Intersection<'a, T: 'a> { inner: IntersectionInner<'a, T>, } +#[derive(Debug)] enum IntersectionInner<'a, T: 'a> { Stitch { + // iterate similarly sized sets jointly, spotting matches along the way a: Iter<'a, T>, b: Iter<'a, T>, }, Search { + // iterate a small set, look up in the large set small_iter: Iter<'a, T>, large_set: &'a BTreeSet<T>, }, + Answer(Option<&'a T>), // return a specific value or emptiness } #[stable(feature = "collection_debug", since = "1.17.0")] impl<T: fmt::Debug> fmt::Debug for Intersection<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.inner { - IntersectionInner::Stitch { - a, - b, - } => f - .debug_tuple("Intersection") - .field(&a) - .field(&b) - .finish(), - IntersectionInner::Search { - small_iter, - large_set: _, - } => f.debug_tuple("Intersection").field(&small_iter).finish(), - } + f.debug_tuple("Intersection").field(&self.inner).finish() } } @@ -224,18 +271,12 @@ /// [`BTreeSet`]: struct.BTreeSet.html /// [`union`]: struct.BTreeSet.html#method.union #[stable(feature = "rust1", since = "1.0.0")] -pub struct Union<'a, T: 'a> { - a: Peekable<Iter<'a, T>>, - b: Peekable<Iter<'a, T>>, -} +pub struct Union<'a, T: 'a>(MergeIterInner<Iter<'a, T>>); #[stable(feature = "collection_debug", since = "1.17.0")] impl<T: fmt::Debug> fmt::Debug for Union<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Union") - .field(&self.a) - .field(&self.b) - .finish() + f.debug_tuple("Union").field(&self.0).finish() } } @@ -314,24 +355,48 @@ /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn difference<'a>(&'a self, other: &'a BTreeSet<T>) -> Difference<'a, T> { - if self.len() > other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF { - // Self is bigger than or not much smaller than other set. - // Iterate both sets jointly, spotting matches along the way. - Difference { - inner: DifferenceInner::Stitch { + let (self_min, self_max) = if let (Some(self_min), Some(self_max)) = + (self.iter().next(), self.iter().next_back()) + { + (self_min, self_max) + } else { + return Difference { + inner: DifferenceInner::Iterate(self.iter()), + }; + }; + let (other_min, other_max) = if let (Some(other_min), Some(other_max)) = + (other.iter().next(), other.iter().next_back()) + { + (other_min, other_max) + } else { + return Difference { + inner: DifferenceInner::Iterate(self.iter()), + }; + }; + Difference { + inner: match (self_min.cmp(other_max), self_max.cmp(other_min)) { + (Greater, _) | (_, Less) => DifferenceInner::Iterate(self.iter()), + (Equal, _) => { + let mut self_iter = self.iter(); + self_iter.next(); + DifferenceInner::Iterate(self_iter) + } + (_, Equal) => { + let mut self_iter = self.iter(); + self_iter.next_back(); + DifferenceInner::Iterate(self_iter) + } + _ if self.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { + DifferenceInner::Search { + self_iter: self.iter(), + other_set: other, + } + } + _ => DifferenceInner::Stitch { self_iter: self.iter(), other_iter: other.iter().peekable(), }, - } - } else { - // Self is much smaller than other set, or both sets are empty. - // Iterate the small set, searching for matches in the large set. - Difference { - inner: DifferenceInner::Search { - self_iter: self.iter(), - other_set: other, - }, - } + }, } } @@ -359,10 +424,7 @@ pub fn symmetric_difference<'a>(&'a self, other: &'a BTreeSet<T>) -> SymmetricDifference<'a, T> { - SymmetricDifference { - a: self.iter().peekable(), - b: other.iter().peekable(), - } + SymmetricDifference(MergeIterInner::new(self.iter(), other.iter())) } /// Visits the values representing the intersection, @@ -387,29 +449,46 @@ /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn intersection<'a>(&'a self, other: &'a BTreeSet<T>) -> Intersection<'a, T> { - let (small, other) = if self.len() <= other.len() { - (self, other) + let (self_min, self_max) = if let (Some(self_min), Some(self_max)) = + (self.iter().next(), self.iter().next_back()) + { + (self_min, self_max) } else { - (other, self) + return Intersection { + inner: IntersectionInner::Answer(None), + }; }; - if small.len() > other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF { - // Small set is not much smaller than other set. - // Iterate both sets jointly, spotting matches along the way. - Intersection { - inner: IntersectionInner::Stitch { - a: small.iter(), + let (other_min, other_max) = if let (Some(other_min), Some(other_max)) = + (other.iter().next(), other.iter().next_back()) + { + (other_min, other_max) + } else { + return Intersection { + inner: IntersectionInner::Answer(None), + }; + }; + Intersection { + inner: match (self_min.cmp(other_max), self_max.cmp(other_min)) { + (Greater, _) | (_, Less) => IntersectionInner::Answer(None), + (Equal, _) => IntersectionInner::Answer(Some(self_min)), + (_, Equal) => IntersectionInner::Answer(Some(self_max)), + _ if self.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { + IntersectionInner::Search { + small_iter: self.iter(), + large_set: other, + } + } + _ if other.len() <= self.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { + IntersectionInner::Search { + small_iter: other.iter(), + large_set: self, + } + } + _ => IntersectionInner::Stitch { + a: self.iter(), b: other.iter(), }, - } - } else { - // Big difference in number of elements, or both sets are empty. - // Iterate the small set, searching for matches in the large set. - Intersection { - inner: IntersectionInner::Search { - small_iter: small.iter(), - large_set: other, - }, - } + }, } } @@ -433,10 +512,7 @@ /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn union<'a>(&'a self, other: &'a BTreeSet<T>) -> Union<'a, T> { - Union { - a: self.iter().peekable(), - b: other.iter().peekable(), - } + Union(MergeIterInner::new(self.iter(), other.iter())) } /// Clears the set, removing all values. @@ -544,43 +620,61 @@ #[stable(feature = "rust1", since = "1.0.0")] pub fn is_subset(&self, other: &BTreeSet<T>) -> bool { // Same result as self.difference(other).next().is_none() - // but the 3 paths below are faster (in order: hugely, 20%, 5%). + // but the code below is faster (hugely in some cases). if self.len() > other.len() { - false - } else if self.len() > other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF { - // Self is not much smaller than other set. - // Stolen from TreeMap - let mut x = self.iter(); - let mut y = other.iter(); - let mut a = x.next(); - let mut b = y.next(); - while a.is_some() { - if b.is_none() { - return false; - } - - let a1 = a.unwrap(); - let b1 = b.unwrap(); - - match b1.cmp(a1) { - Less => (), - Greater => return false, - Equal => a = x.next(), - } - - b = y.next(); - } - true + return false; + } + let (self_min, self_max) = if let (Some(self_min), Some(self_max)) = + (self.iter().next(), self.iter().next_back()) + { + (self_min, self_max) } else { - // Big difference in number of elements, or both sets are empty. - // Iterate the small set, searching for matches in the large set. - for next in self { + return true; // self is empty + }; + let (other_min, other_max) = if let (Some(other_min), Some(other_max)) = + (other.iter().next(), other.iter().next_back()) + { + (other_min, other_max) + } else { + return false; // other is empty + }; + let mut self_iter = self.iter(); + match self_min.cmp(other_min) { + Less => return false, + Equal => { + self_iter.next(); + } + Greater => (), + } + match self_max.cmp(other_max) { + Greater => return false, + Equal => { + self_iter.next_back(); + } + Less => (), + } + if self_iter.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF { + // Big difference in number of elements. + for next in self_iter { if !other.contains(next) { return false; } } - true + } else { + // Self is not much smaller than other set. + let mut other_iter = other.iter(); + other_iter.next(); + other_iter.next_back(); + let mut self_next = self_iter.next(); + while let Some(self1) = self_next { + match other_iter.next().map_or(Less, |other1| self1.cmp(other1)) { + Less => return false, + Equal => self_next = self_iter.next(), + Greater => (), + } + } } + true } /// Returns `true` if the set is a superset of another, @@ -1092,15 +1186,6 @@ #[stable(feature = "fused", since = "1.26.0")] impl<T> FusedIterator for Range<'_, T> {} -/// Compares `x` and `y`, but return `short` if x is None and `long` if y is None -fn cmp_opt<T: Ord>(x: Option<&T>, y: Option<&T>, short: Ordering, long: Ordering) -> Ordering { - match (x, y) { - (None, _) => short, - (_, None) => long, - (Some(x1), Some(y1)) => x1.cmp(y1), - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl<T> Clone for Difference<'_, T> { fn clone(&self) -> Self { @@ -1120,6 +1205,7 @@ self_iter: self_iter.clone(), other_set, }, + DifferenceInner::Iterate(iter) => DifferenceInner::Iterate(iter.clone()), }, } } @@ -1138,7 +1224,7 @@ loop { match other_iter .peek() - .map_or(Less, |other_next| Ord::cmp(self_next, other_next)) + .map_or(Less, |other_next| self_next.cmp(other_next)) { Less => return Some(self_next), Equal => { @@ -1160,6 +1246,7 @@ return Some(self_next); } }, + DifferenceInner::Iterate(iter) => iter.next(), } } @@ -1167,12 +1254,13 @@ let (self_len, other_len) = match &self.inner { DifferenceInner::Stitch { self_iter, - other_iter + other_iter, } => (self_iter.len(), other_iter.len()), DifferenceInner::Search { self_iter, - other_set + other_set, } => (self_iter.len(), other_set.len()), + DifferenceInner::Iterate(iter) => (iter.len(), 0), }; (self_len.saturating_sub(other_len), Some(self_len)) } @@ -1184,10 +1272,7 @@ #[stable(feature = "rust1", since = "1.0.0")] impl<T> Clone for SymmetricDifference<'_, T> { fn clone(&self) -> Self { - SymmetricDifference { - a: self.a.clone(), - b: self.b.clone(), - } + SymmetricDifference(self.0.clone()) } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1196,19 +1281,19 @@ fn next(&mut self) -> Option<&'a T> { loop { - match cmp_opt(self.a.peek(), self.b.peek(), Greater, Less) { - Less => return self.a.next(), - Equal => { - self.a.next(); - self.b.next(); - } - Greater => return self.b.next(), + let (a_next, b_next) = self.0.nexts(); + if a_next.and(b_next).is_none() { + return a_next.or(b_next); } } } fn size_hint(&self) -> (usize, Option<usize>) { - (0, Some(self.a.len() + self.b.len())) + let (a_len, b_len) = self.0.lens(); + // No checked_add, because even if a and b refer to the same set, + // and T is an empty type, the storage overhead of sets limits + // the number of elements to less than half the range of usize. + (0, Some(a_len + b_len)) } } @@ -1234,6 +1319,7 @@ small_iter: small_iter.clone(), large_set, }, + IntersectionInner::Answer(answer) => IntersectionInner::Answer(*answer), }, } } @@ -1251,7 +1337,7 @@ let mut a_next = a.next()?; let mut b_next = b.next()?; loop { - match Ord::cmp(a_next, b_next) { + match a_next.cmp(b_next) { Less => a_next = a.next()?, Greater => b_next = b.next()?, Equal => return Some(a_next), @@ -1267,15 +1353,17 @@ return Some(small_next); } }, + IntersectionInner::Answer(answer) => answer.take(), } } fn size_hint(&self) -> (usize, Option<usize>) { - let min_len = match &self.inner { - IntersectionInner::Stitch { a, b } => min(a.len(), b.len()), - IntersectionInner::Search { small_iter, .. } => small_iter.len(), - }; - (0, Some(min_len)) + match &self.inner { + IntersectionInner::Stitch { a, b } => (0, Some(min(a.len(), b.len()))), + IntersectionInner::Search { small_iter, .. } => (0, Some(small_iter.len())), + IntersectionInner::Answer(None) => (0, Some(0)), + IntersectionInner::Answer(Some(_)) => (1, Some(1)), + } } } @@ -1285,10 +1373,7 @@ #[stable(feature = "rust1", since = "1.0.0")] impl<T> Clone for Union<'_, T> { fn clone(&self) -> Self { - Union { - a: self.a.clone(), - b: self.b.clone(), - } + Union(self.0.clone()) } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1296,19 +1381,13 @@ type Item = &'a T; fn next(&mut self) -> Option<&'a T> { - match cmp_opt(self.a.peek(), self.b.peek(), Greater, Less) { - Less => self.a.next(), - Equal => { - self.b.next(); - self.a.next() - } - Greater => self.b.next(), - } + let (a_next, b_next) = self.0.nexts(); + a_next.or(b_next) } fn size_hint(&self) -> (usize, Option<usize>) { - let a_len = self.a.len(); - let b_len = self.b.len(); + let (a_len, b_len) = self.0.lens(); + // No checked_add - see SymmetricDifference::size_hint. (max(a_len, b_len), Some(a_len + b_len)) } }
diff --git a/src/liballoc/collections/linked_list.rs b/src/liballoc/collections/linked_list.rs index 816a71f..702df25 100644 --- a/src/liballoc/collections/linked_list.rs +++ b/src/liballoc/collections/linked_list.rs
@@ -1197,6 +1197,19 @@ fn clone(&self) -> Self { self.iter().cloned().collect() } + + fn clone_from(&mut self, other: &Self) { + let mut iter_other = other.iter(); + if self.len() > other.len() { + self.split_off(other.len()); + } + for (elem, elem_other) in self.iter_mut().zip(&mut iter_other) { + elem.clone_from(elem_other); + } + if !iter_other.is_empty() { + self.extend(iter_other.cloned()); + } + } } #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/src/liballoc/collections/linked_list/tests.rs b/src/liballoc/collections/linked_list/tests.rs index ecb5948..1001f6b 100644 --- a/src/liballoc/collections/linked_list/tests.rs +++ b/src/liballoc/collections/linked_list/tests.rs
@@ -111,6 +111,49 @@ } #[test] +fn test_clone_from() { + // Short cloned from long + { + let v = vec![1, 2, 3, 4, 5]; + let u = vec![8, 7, 6, 2, 3, 4, 5]; + let mut m = list_from(&v); + let n = list_from(&u); + m.clone_from(&n); + check_links(&m); + assert_eq!(m, n); + for elt in u { + assert_eq!(m.pop_front(), Some(elt)) + } + } + // Long cloned from short + { + let v = vec![1, 2, 3, 4, 5]; + let u = vec![6, 7, 8]; + let mut m = list_from(&v); + let n = list_from(&u); + m.clone_from(&n); + check_links(&m); + assert_eq!(m, n); + for elt in u { + assert_eq!(m.pop_front(), Some(elt)) + } + } + // Two equal length lists + { + let v = vec![1, 2, 3, 4, 5]; + let u = vec![9, 8, 1, 2, 3]; + let mut m = list_from(&v); + let n = list_from(&u); + m.clone_from(&n); + check_links(&m); + assert_eq!(m, n); + for elt in u { + assert_eq!(m.pop_front(), Some(elt)) + } + } +} + +#[test] fn test_insert_prev() { let mut m = list_from(&[0, 2, 4, 6, 8]); let len = m.len();
diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index a4a0fbb..8f3dfab 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs
@@ -10,8 +10,8 @@ use core::array::LengthAtMost32; use core::cmp::{self, Ordering}; use core::fmt; -use core::iter::{repeat_with, FromIterator, FusedIterator}; -use core::mem; +use core::iter::{once, repeat_with, FromIterator, FusedIterator}; +use core::mem::{self, replace}; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{Index, IndexMut, RangeBounds, Try}; use core::ptr::{self, NonNull}; @@ -57,11 +57,88 @@ buf: RawVec<T>, } +/// PairSlices pairs up equal length slice parts of two deques +/// +/// For example, given deques "A" and "B" with the following division into slices: +/// +/// A: [0 1 2] [3 4 5] +/// B: [a b] [c d e] +/// +/// It produces the following sequence of matching slices: +/// +/// ([0 1], [a b]) +/// ([2], [c]) +/// ([3 4], [d e]) +/// +/// and the uneven remainder of either A or B is skipped. +struct PairSlices<'a, 'b, T> { + a0: &'a mut [T], + a1: &'a mut [T], + b0: &'b [T], + b1: &'b [T], +} + +impl<'a, 'b, T> PairSlices<'a, 'b, T> { + fn from(to: &'a mut VecDeque<T>, from: &'b VecDeque<T>) -> Self { + let (a0, a1) = to.as_mut_slices(); + let (b0, b1) = from.as_slices(); + PairSlices { a0, a1, b0, b1 } + } + + fn has_remainder(&self) -> bool { + !self.b0.is_empty() + } + + fn remainder(self) -> impl Iterator<Item=&'b [T]> { + once(self.b0).chain(once(self.b1)) + } +} + +impl<'a, 'b, T> Iterator for PairSlices<'a, 'b, T> +{ + type Item = (&'a mut [T], &'b [T]); + fn next(&mut self) -> Option<Self::Item> { + // Get next part length + let part = cmp::min(self.a0.len(), self.b0.len()); + if part == 0 { + return None; + } + let (p0, p1) = replace(&mut self.a0, &mut []).split_at_mut(part); + let (q0, q1) = self.b0.split_at(part); + + // Move a1 into a0, if it's empty (and b1, b0 the same way). + self.a0 = p1; + self.b0 = q1; + if self.a0.is_empty() { + self.a0 = replace(&mut self.a1, &mut []); + } + if self.b0.is_empty() { + self.b0 = replace(&mut self.b1, &[]); + } + Some((p0, q0)) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<T: Clone> Clone for VecDeque<T> { fn clone(&self) -> VecDeque<T> { self.iter().cloned().collect() } + + fn clone_from(&mut self, other: &Self) { + self.truncate(other.len()); + + let mut iter = PairSlices::from(self, other); + while let Some((dst, src)) = iter.next() { + dst.clone_from_slice(&src); + } + + if iter.has_remainder() { + for remainder in iter.remainder() { + self.extend(remainder.iter().cloned()); + } + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1740,7 +1817,7 @@ } } - return elem; + elem } /// Splits the `VecDeque` into two at the given index. @@ -2209,6 +2286,16 @@ final_res } + fn nth(&mut self, n: usize) -> Option<Self::Item> { + if n >= count(self.tail, self.head, self.ring.len()) { + self.tail = self.head; + None + } else { + self.tail = wrap_index(self.tail.wrapping_add(n), self.ring.len()); + self.next() + } + } + #[inline] fn last(mut self) -> Option<&'a T> { self.next_back() @@ -2327,6 +2414,16 @@ back.iter_mut().fold(accum, &mut f) } + fn nth(&mut self, n: usize) -> Option<Self::Item> { + if n >= count(self.tail, self.head, self.ring.len()) { + self.tail = self.head; + None + } else { + self.tail = wrap_index(self.tail.wrapping_add(n), self.ring.len()); + self.next() + } + } + #[inline] fn last(mut self) -> Option<&'a mut T> { self.next_back()
diff --git a/src/liballoc/collections/vec_deque/tests.rs b/src/liballoc/collections/vec_deque/tests.rs index d253523..d578ee0 100644 --- a/src/liballoc/collections/vec_deque/tests.rs +++ b/src/liballoc/collections/vec_deque/tests.rs
@@ -362,6 +362,29 @@ } #[test] +fn test_clone_from() { + let m = vec![1; 8]; + let n = vec![2; 12]; + for pfv in 0..8 { + for pfu in 0..8 { + for longer in 0..2 { + let (vr, ur) = if longer == 0 { (&m, &n) } else { (&n, &m) }; + let mut v = VecDeque::from(vr.clone()); + for _ in 0..pfv { + v.push_front(1); + } + let mut u = VecDeque::from(ur.clone()); + for _ in 0..pfu { + u.push_front(2); + } + v.clone_from(&u); + assert_eq!(&v, &u); + } + } + } +} + +#[test] fn issue_53529() { use crate::boxed::Box;
diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index 68cbc36..cbfc552 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs
@@ -80,24 +80,210 @@ //! arguments which have names. Like with positional parameters, it is not //! valid to provide named parameters that are unused by the format string. //! -//! ## Argument types +//! # Formatting Parameters //! -//! Each argument's type is dictated by the format string. -//! There are various parameters which require a particular type, however. -//! An example is the `{:.*}` syntax, which sets the number of decimal places -//! in floating-point types: +//! Each argument being formatted can be transformed by a number of formatting +//! parameters (corresponding to `format_spec` in the syntax above). These +//! parameters affect the string representation of what's being formatted. +//! +//! ## Width //! //! ``` -//! let formatted_number = format!("{:.*}", 2, 1.234567); -//! -//! assert_eq!("1.23", formatted_number) +//! // All of these print "Hello x !" +//! println!("Hello {:5}!", "x"); +//! println!("Hello {:1$}!", "x", 5); +//! println!("Hello {1:0$}!", 5, "x"); +//! println!("Hello {:width$}!", "x", width = 5); //! ``` //! -//! If this syntax is used, then the number of characters to print precedes the -//! actual object being formatted, and the number of characters must have the -//! type [`usize`]. +//! This is a parameter for the "minimum width" that the format should take up. +//! If the value's string does not fill up this many characters, then the +//! padding specified by fill/alignment will be used to take up the required +//! space (see below). //! -//! ## Formatting traits +//! The value for the width can also be provided as a [`usize`] in the list of +//! parameters by adding a postfix `$`, indicating that the second argument is +//! a [`usize`] specifying the width. +//! +//! Referring to an argument with the dollar syntax does not affect the "next +//! argument" counter, so it's usually a good idea to refer to arguments by +//! position, or use named arguments. +//! +//! ## Fill/Alignment +//! +//! ``` +//! assert_eq!(format!("Hello {:<5}!", "x"), "Hello x !"); +//! assert_eq!(format!("Hello {:-<5}!", "x"), "Hello x----!"); +//! assert_eq!(format!("Hello {:^5}!", "x"), "Hello x !"); +//! assert_eq!(format!("Hello {:>5}!", "x"), "Hello x!"); +//! ``` +//! +//! The optional fill character and alignment is provided normally in conjunction with the +//! [`width`](#width) parameter. It must be defined before `width`, right after the `:`. +//! This indicates that if the value being formatted is smaller than +//! `width` some extra characters will be printed around it. +//! Filling comes in the following variants for different alignments: +//! +//! * `[fill]<` - the argument is left-aligned in `width` columns +//! * `[fill]^` - the argument is center-aligned in `width` columns +//! * `[fill]>` - the argument is right-aligned in `width` columns +//! +//! The default [fill/alignment](#fillalignment) for non-numerics is a space and +//! left-aligned. The +//! defaults for numeric formatters is also a space but with right-alignment. If +//! the `0` flag (see below) is specified for numerics, then the implicit fill character is +//! `0`. +//! +//! Note that alignment may not be implemented by some types. In particular, it +//! is not generally implemented for the `Debug` trait. A good way to ensure +//! padding is applied is to format your input, then pad this resulting string +//! to obtain your output: +//! +//! ``` +//! println!("Hello {:^15}!", format!("{:?}", Some("hi"))); // => "Hello Some("hi") !" +//! ``` +//! +//! ## Sign/`#`/`0` +//! +//! ``` +//! assert_eq!(format!("Hello {:+}!", 5), "Hello +5!"); +//! assert_eq!(format!("{:#x}!", 27), "0x1b!"); +//! assert_eq!(format!("Hello {:05}!", 5), "Hello 00005!"); +//! assert_eq!(format!("Hello {:05}!", -5), "Hello -0005!"); +//! assert_eq!(format!("{:#010x}!", 27), "0x0000001b!"); +//! ``` +//! +//! These are all flags altering the behavior of the formatter. +//! +//! * `+` - This is intended for numeric types and indicates that the sign +//! should always be printed. Positive signs are never printed by +//! default, and the negative sign is only printed by default for the +//! `Signed` trait. This flag indicates that the correct sign (`+` or `-`) +//! should always be printed. +//! * `-` - Currently not used +//! * `#` - This flag is indicates that the "alternate" form of printing should +//! be used. The alternate forms are: +//! * `#?` - pretty-print the [`Debug`] formatting +//! * `#x` - precedes the argument with a `0x` +//! * `#X` - precedes the argument with a `0x` +//! * `#b` - precedes the argument with a `0b` +//! * `#o` - precedes the argument with a `0o` +//! * `0` - This is used to indicate for integer formats that the padding to `width` should +//! both be done with a `0` character as well as be sign-aware. A format +//! like `{:08}` would yield `00000001` for the integer `1`, while the +//! same format would yield `-0000001` for the integer `-1`. Notice that +//! the negative version has one fewer zero than the positive version. +//! Note that padding zeroes are always placed after the sign (if any) +//! and before the digits. When used together with the `#` flag, a similar +//! rule applies: padding zeroes are inserted after the prefix but before +//! the digits. The prefix is included in the total width. +//! +//! ## Precision +//! +//! For non-numeric types, this can be considered a "maximum width". If the resulting string is +//! longer than this width, then it is truncated down to this many characters and that truncated +//! value is emitted with proper `fill`, `alignment` and `width` if those parameters are set. +//! +//! For integral types, this is ignored. +//! +//! For floating-point types, this indicates how many digits after the decimal point should be +//! printed. +//! +//! There are three possible ways to specify the desired `precision`: +//! +//! 1. An integer `.N`: +//! +//! the integer `N` itself is the precision. +//! +//! 2. An integer or name followed by dollar sign `.N$`: +//! +//! use format *argument* `N` (which must be a `usize`) as the precision. +//! +//! 3. An asterisk `.*`: +//! +//! `.*` means that this `{...}` is associated with *two* format inputs rather than one: the +//! first input holds the `usize` precision, and the second holds the value to print. Note that +//! in this case, if one uses the format string `{<arg>:<spec>.*}`, then the `<arg>` part refers +//! to the *value* to print, and the `precision` must come in the input preceding `<arg>`. +//! +//! For example, the following calls all print the same thing `Hello x is 0.01000`: +//! +//! ``` +//! // Hello {arg 0 ("x")} is {arg 1 (0.01) with precision specified inline (5)} +//! println!("Hello {0} is {1:.5}", "x", 0.01); +//! +//! // Hello {arg 1 ("x")} is {arg 2 (0.01) with precision specified in arg 0 (5)} +//! println!("Hello {1} is {2:.0$}", 5, "x", 0.01); +//! +//! // Hello {arg 0 ("x")} is {arg 2 (0.01) with precision specified in arg 1 (5)} +//! println!("Hello {0} is {2:.1$}", "x", 5, 0.01); +//! +//! // Hello {next arg ("x")} is {second of next two args (0.01) with precision +//! // specified in first of next two args (5)} +//! println!("Hello {} is {:.*}", "x", 5, 0.01); +//! +//! // Hello {next arg ("x")} is {arg 2 (0.01) with precision +//! // specified in its predecessor (5)} +//! println!("Hello {} is {2:.*}", "x", 5, 0.01); +//! +//! // Hello {next arg ("x")} is {arg "number" (0.01) with precision specified +//! // in arg "prec" (5)} +//! println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); +//! ``` +//! +//! While these: +//! +//! ``` +//! println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); +//! println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); +//! println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); +//! ``` +//! +//! print two significantly different things: +//! +//! ```text +//! Hello, `1234.560` has 3 fractional digits +//! Hello, `123` has 3 characters +//! Hello, ` 123` has 3 right-aligned characters +//! ``` +//! +//! # Escaping +//! +//! The literal characters `{` and `}` may be included in a string by preceding +//! them with the same character. For example, the `{` character is escaped with +//! `{{` and the `}` character is escaped with `}}`. +//! +//! ``` +//! assert_eq!(format!("Hello {{}}"), "Hello {}"); +//! assert_eq!(format!("{{ Hello"), "{ Hello"); +//! ``` +//! +//! # Syntax +//! +//! To summarize, here you can find the full grammar of format strings. +//! The syntax for the formatting language used is drawn from other languages, +//! so it should not be too alien. Arguments are formatted with Python-like +//! syntax, meaning that arguments are surrounded by `{}` instead of the C-like +//! `%`. The actual grammar for the formatting syntax is: +//! +//! ```text +//! format_string := <text> [ maybe-format <text> ] * +//! maybe-format := '{' '{' | '}' '}' | <format> +//! format := '{' [ argument ] [ ':' format_spec ] '}' +//! argument := integer | identifier +//! +//! format_spec := [[fill]align][sign]['#']['0'][width]['.' precision][type] +//! fill := character +//! align := '<' | '^' | '>' +//! sign := '+' | '-' +//! width := count +//! precision := count | '*' +//! type := identifier | '?' | '' +//! count := parameter | integer +//! parameter := argument '$' +//! ``` +//! +//! # Formatting traits //! //! When requesting that an argument be formatted with a particular type, you //! are actually requesting that an argument ascribes to a particular trait. @@ -220,7 +406,7 @@ //! assert_eq!(format!("{} {:?}", "foo\n", "bar\n"), "foo\n \"bar\\n\""); //! ``` //! -//! ## Related macros +//! # Related macros //! //! There are a number of related macros in the [`format!`] family. The ones that //! are currently implemented are: @@ -300,185 +486,6 @@ //! it would internally pass around this structure until it has been determined //! where output should go to. //! -//! # Syntax -//! -//! The syntax for the formatting language used is drawn from other languages, -//! so it should not be too alien. Arguments are formatted with Python-like -//! syntax, meaning that arguments are surrounded by `{}` instead of the C-like -//! `%`. The actual grammar for the formatting syntax is: -//! -//! ```text -//! format_string := <text> [ maybe-format <text> ] * -//! maybe-format := '{' '{' | '}' '}' | <format> -//! format := '{' [ argument ] [ ':' format_spec ] '}' -//! argument := integer | identifier -//! -//! format_spec := [[fill]align][sign]['#']['0'][width]['.' precision][type] -//! fill := character -//! align := '<' | '^' | '>' -//! sign := '+' | '-' -//! width := count -//! precision := count | '*' -//! type := identifier | '?' | '' -//! count := parameter | integer -//! parameter := argument '$' -//! ``` -//! -//! # Formatting Parameters -//! -//! Each argument being formatted can be transformed by a number of formatting -//! parameters (corresponding to `format_spec` in the syntax above). These -//! parameters affect the string representation of what's being formatted. -//! -//! ## Fill/Alignment -//! -//! The fill character is provided normally in conjunction with the -//! [`width`](#width) -//! parameter. This indicates that if the value being formatted is smaller than -//! `width` some extra characters will be printed around it. The extra -//! characters are specified by `fill`, and the alignment can be one of the -//! following options: -//! -//! * `<` - the argument is left-aligned in `width` columns -//! * `^` - the argument is center-aligned in `width` columns -//! * `>` - the argument is right-aligned in `width` columns -//! -//! Note that alignment may not be implemented by some types. In particular, it -//! is not generally implemented for the `Debug` trait. A good way to ensure -//! padding is applied is to format your input, then use this resulting string -//! to pad your output. -//! -//! ## Sign/`#`/`0` -//! -//! These can all be interpreted as flags for a particular formatter. -//! -//! * `+` - This is intended for numeric types and indicates that the sign -//! should always be printed. Positive signs are never printed by -//! default, and the negative sign is only printed by default for the -//! `Signed` trait. This flag indicates that the correct sign (`+` or `-`) -//! should always be printed. -//! * `-` - Currently not used -//! * `#` - This flag is indicates that the "alternate" form of printing should -//! be used. The alternate forms are: -//! * `#?` - pretty-print the [`Debug`] formatting -//! * `#x` - precedes the argument with a `0x` -//! * `#X` - precedes the argument with a `0x` -//! * `#b` - precedes the argument with a `0b` -//! * `#o` - precedes the argument with a `0o` -//! * `0` - This is used to indicate for integer formats that the padding should -//! both be done with a `0` character as well as be sign-aware. A format -//! like `{:08}` would yield `00000001` for the integer `1`, while the -//! same format would yield `-0000001` for the integer `-1`. Notice that -//! the negative version has one fewer zero than the positive version. -//! Note that padding zeroes are always placed after the sign (if any) -//! and before the digits. When used together with the `#` flag, a similar -//! rule applies: padding zeroes are inserted after the prefix but before -//! the digits. -//! -//! ## Width -//! -//! This is a parameter for the "minimum width" that the format should take up. -//! If the value's string does not fill up this many characters, then the -//! padding specified by fill/alignment will be used to take up the required -//! space. -//! -//! The default [fill/alignment](#fillalignment) for non-numerics is a space and -//! left-aligned. The -//! defaults for numeric formatters is also a space but with right-alignment. If -//! the `0` flag is specified for numerics, then the implicit fill character is -//! `0`. -//! -//! The value for the width can also be provided as a [`usize`] in the list of -//! parameters by using the dollar syntax indicating that the second argument is -//! a [`usize`] specifying the width, for example: -//! -//! ``` -//! // All of these print "Hello x !" -//! println!("Hello {:5}!", "x"); -//! println!("Hello {:1$}!", "x", 5); -//! println!("Hello {1:0$}!", 5, "x"); -//! println!("Hello {:width$}!", "x", width = 5); -//! ``` -//! -//! Referring to an argument with the dollar syntax does not affect the "next -//! argument" counter, so it's usually a good idea to refer to arguments by -//! position, or use named arguments. -//! -//! ## Precision -//! -//! For non-numeric types, this can be considered a "maximum width". If the resulting string is -//! longer than this width, then it is truncated down to this many characters and that truncated -//! value is emitted with proper `fill`, `alignment` and `width` if those parameters are set. -//! -//! For integral types, this is ignored. -//! -//! For floating-point types, this indicates how many digits after the decimal point should be -//! printed. -//! -//! There are three possible ways to specify the desired `precision`: -//! -//! 1. An integer `.N`: -//! -//! the integer `N` itself is the precision. -//! -//! 2. An integer or name followed by dollar sign `.N$`: -//! -//! use format *argument* `N` (which must be a `usize`) as the precision. -//! -//! 3. An asterisk `.*`: -//! -//! `.*` means that this `{...}` is associated with *two* format inputs rather than one: the -//! first input holds the `usize` precision, and the second holds the value to print. Note that -//! in this case, if one uses the format string `{<arg>:<spec>.*}`, then the `<arg>` part refers -//! to the *value* to print, and the `precision` must come in the input preceding `<arg>`. -//! -//! For example, the following calls all print the same thing `Hello x is 0.01000`: -//! -//! ``` -//! // Hello {arg 0 ("x")} is {arg 1 (0.01) with precision specified inline (5)} -//! println!("Hello {0} is {1:.5}", "x", 0.01); -//! -//! // Hello {arg 1 ("x")} is {arg 2 (0.01) with precision specified in arg 0 (5)} -//! println!("Hello {1} is {2:.0$}", 5, "x", 0.01); -//! -//! // Hello {arg 0 ("x")} is {arg 2 (0.01) with precision specified in arg 1 (5)} -//! println!("Hello {0} is {2:.1$}", "x", 5, 0.01); -//! -//! // Hello {next arg ("x")} is {second of next two args (0.01) with precision -//! // specified in first of next two args (5)} -//! println!("Hello {} is {:.*}", "x", 5, 0.01); -//! -//! // Hello {next arg ("x")} is {arg 2 (0.01) with precision -//! // specified in its predecessor (5)} -//! println!("Hello {} is {2:.*}", "x", 5, 0.01); -//! -//! // Hello {next arg ("x")} is {arg "number" (0.01) with precision specified -//! // in arg "prec" (5)} -//! println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); -//! ``` -//! -//! While these: -//! -//! ``` -//! println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); -//! println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); -//! println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); -//! ``` -//! -//! print two significantly different things: -//! -//! ```text -//! Hello, `1234.560` has 3 fractional digits -//! Hello, `123` has 3 characters -//! Hello, ` 123` has 3 right-aligned characters -//! ``` -//! -//! # Escaping -//! -//! The literal characters `{` and `}` may be included in a string by preceding -//! them with the same character. For example, the `{` character is escaped with -//! `{{` and the `}` character is escaped with `}}`. -//! //! [`usize`]: ../../std/primitive.usize.html //! [`isize`]: ../../std/primitive.isize.html //! [`i8`]: ../../std/primitive.i8.html
diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 9e6ed92..94379af 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs
@@ -85,6 +85,7 @@ #![feature(const_generic_impls_guard)] #![feature(const_generics)] #![feature(const_in_array_repeat_expressions)] +#![feature(cow_is_borrowed)] #![feature(dispatch_from_dyn)] #![feature(core_intrinsics)] #![feature(container_error_extra)] @@ -117,12 +118,10 @@ #![feature(allocator_internals)] #![feature(on_unimplemented)] #![feature(rustc_const_unstable)] -#![cfg_attr(bootstrap, feature(const_vec_new))] #![feature(slice_partition_dedup)] #![feature(maybe_uninit_extra, maybe_uninit_slice)] #![feature(alloc_layout_extra)] #![feature(try_trait)] -#![feature(mem_take)] #![feature(associated_type_bounds)] // Allow testing this library @@ -155,7 +154,7 @@ #[cfg(test)] mod tests; pub mod collections; -#[cfg(all(target_has_atomic = "ptr", target_has_atomic = "cas"))] +#[cfg(target_has_atomic = "ptr")] pub mod sync; pub mod rc; pub mod raw_vec;
diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index f234ac5..f1c4c32 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs
@@ -3,8 +3,9 @@ //! //! The type [`Rc<T>`][`Rc`] provides shared ownership of a value of type `T`, //! allocated in the heap. Invoking [`clone`][clone] on [`Rc`] produces a new -//! pointer to the same value in the heap. When the last [`Rc`] pointer to a -//! given value is destroyed, the pointed-to value is also destroyed. +//! pointer to the same allocation in the heap. When the last [`Rc`] pointer to a +//! given allocation is destroyed, the value stored in that allocation (often +//! referred to as "inner value") is also dropped. //! //! Shared references in Rust disallow mutation by default, and [`Rc`] //! is no exception: you cannot generally obtain a mutable reference to @@ -21,8 +22,10 @@ //! //! The [`downgrade`][downgrade] method can be used to create a non-owning //! [`Weak`] pointer. A [`Weak`] pointer can be [`upgrade`][upgrade]d -//! to an [`Rc`], but this will return [`None`] if the value has -//! already been dropped. +//! to an [`Rc`], but this will return [`None`] if the value stored in the allocation has +//! already been dropped. In other words, `Weak` pointers do not keep the value +//! inside the allocation alive; however, they *do* keep the allocation +//! (the backing store for the inner value) alive. //! //! A cycle between [`Rc`] pointers will never be deallocated. For this reason, //! [`Weak`] is used to break cycles. For example, a tree could have strong @@ -41,13 +44,13 @@ //! Rc::downgrade(&my_rc); //! ``` //! -//! [`Weak<T>`][`Weak`] does not auto-dereference to `T`, because the value may have -//! already been destroyed. +//! [`Weak<T>`][`Weak`] does not auto-dereference to `T`, because the inner value may have +//! already been dropped. //! //! # Cloning references //! -//! Creating a new reference from an existing reference counted pointer is done using the -//! `Clone` trait implemented for [`Rc<T>`][`Rc`] and [`Weak<T>`][`Weak`]. +//! Creating a new reference to the same allocation as an existing reference counted pointer +//! is done using the `Clone` trait implemented for [`Rc<T>`][`Rc`] and [`Weak<T>`][`Weak`]. //! //! ``` //! use std::rc::Rc; @@ -93,7 +96,7 @@ //! ); //! //! // Create `Gadget`s belonging to `gadget_owner`. Cloning the `Rc<Owner>` -//! // value gives us a new pointer to the same `Owner` value, incrementing +//! // gives us a new pointer to the same `Owner` allocation, incrementing //! // the reference count in the process. //! let gadget1 = Gadget { //! id: 1, @@ -110,8 +113,8 @@ //! // Despite dropping `gadget_owner`, we're still able to print out the name //! // of the `Owner` of the `Gadget`s. This is because we've only dropped a //! // single `Rc<Owner>`, not the `Owner` it points to. As long as there are -//! // other `Rc<Owner>` values pointing at the same `Owner`, it will remain -//! // allocated. The field projection `gadget1.owner.name` works because +//! // other `Rc<Owner>` pointing at the same `Owner` allocation, it will remain +//! // live. The field projection `gadget1.owner.name` works because //! // `Rc<Owner>` automatically dereferences to `Owner`. //! println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name); //! println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name); @@ -124,9 +127,9 @@ //! //! If our requirements change, and we also need to be able to traverse from //! `Owner` to `Gadget`, we will run into problems. An [`Rc`] pointer from `Owner` -//! to `Gadget` introduces a cycle between the values. This means that their -//! reference counts can never reach 0, and the values will remain allocated -//! forever: a memory leak. In order to get around this, we can use [`Weak`] +//! to `Gadget` introduces a cycle. This means that their +//! reference counts can never reach 0, and the allocation will never be destroyed: +//! a memory leak. In order to get around this, we can use [`Weak`] //! pointers. //! //! Rust actually makes it somewhat difficult to produce this loop in the first @@ -193,10 +196,10 @@ //! for gadget_weak in gadget_owner.gadgets.borrow().iter() { //! //! // `gadget_weak` is a `Weak<Gadget>`. Since `Weak` pointers can't -//! // guarantee the value is still allocated, we need to call +//! // guarantee the allocation still exists, we need to call //! // `upgrade`, which returns an `Option<Rc<Gadget>>`. //! // -//! // In this case we know the value still exists, so we simply +//! // In this case we know the allocation still exists, so we simply //! // `unwrap` the `Option`. In a more complicated program, you might //! // need graceful error handling for a `None` result. //! @@ -365,7 +368,7 @@ unsafe { Pin::new_unchecked(Rc::new(value)) } } - /// Returns the contained value, if the `Rc` has exactly one strong reference. + /// Returns the inner value, if the `Rc` has exactly one strong reference. /// /// Otherwise, an [`Err`][result] is returned with the same `Rc` that was /// passed in. @@ -446,7 +449,7 @@ /// # Safety /// /// As with [`MaybeUninit::assume_init`], - /// it is up to the caller to guarantee that the value + /// it is up to the caller to guarantee that the inner value /// really is in an initialized state. /// Calling this when the content is not yet fully initialized /// causes immediate undefined behavior. @@ -485,7 +488,7 @@ /// # Safety /// /// As with [`MaybeUninit::assume_init`], - /// it is up to the caller to guarantee that the value + /// it is up to the caller to guarantee that the inner value /// really is in an initialized state. /// Calling this when the content is not yet fully initialized /// causes immediate undefined behavior. @@ -604,7 +607,7 @@ unsafe { NonNull::new_unchecked(Rc::into_raw(this) as *mut _) } } - /// Creates a new [`Weak`][weak] pointer to this value. + /// Creates a new [`Weak`][weak] pointer to this allocation. /// /// [weak]: struct.Weak.html /// @@ -625,7 +628,7 @@ Weak { ptr: this.ptr } } - /// Gets the number of [`Weak`][weak] pointers to this value. + /// Gets the number of [`Weak`][weak] pointers to this allocation. /// /// [weak]: struct.Weak.html /// @@ -645,7 +648,7 @@ this.weak() - 1 } - /// Gets the number of strong (`Rc`) pointers to this value. + /// Gets the number of strong (`Rc`) pointers to this allocation. /// /// # Examples /// @@ -664,7 +667,7 @@ } /// Returns `true` if there are no other `Rc` or [`Weak`][weak] pointers to - /// this inner value. + /// this allocation. /// /// [weak]: struct.Weak.html #[inline] @@ -672,14 +675,14 @@ Rc::weak_count(this) == 0 && Rc::strong_count(this) == 1 } - /// Returns a mutable reference to the inner value, if there are - /// no other `Rc` or [`Weak`][weak] pointers to the same value. + /// Returns a mutable reference into the given `Rc`, if there are + /// no other `Rc` or [`Weak`][weak] pointers to the same allocation. /// /// Returns [`None`] otherwise, because it is not safe to /// mutate a shared value. /// /// See also [`make_mut`][make_mut], which will [`clone`][clone] - /// the inner value when it's shared. + /// the inner value when there are other pointers. /// /// [weak]: struct.Weak.html /// [`None`]: ../../std/option/enum.Option.html#variant.None @@ -710,7 +713,7 @@ } } - /// Returns a mutable reference to the inner value, + /// Returns a mutable reference into the given `Rc`, /// without any check. /// /// See also [`get_mut`], which is safe and does appropriate checks. @@ -719,7 +722,7 @@ /// /// # Safety /// - /// Any other `Rc` or [`Weak`] pointers to the same value must not be dereferenced + /// Any other `Rc` or [`Weak`] pointers to the same allocation must not be dereferenced /// for the duration of the returned borrow. /// This is trivially the case if no such pointers exist, /// for example immediately after `Rc::new`. @@ -745,8 +748,8 @@ #[inline] #[stable(feature = "ptr_eq", since = "1.17.0")] - /// Returns `true` if the two `Rc`s point to the same value (not - /// just values that compare as equal). + /// Returns `true` if the two `Rc`s point to the same allocation + /// (in a vein similar to [`ptr::eq`]). /// /// # Examples /// @@ -760,6 +763,8 @@ /// assert!(Rc::ptr_eq(&five, &same_five)); /// assert!(!Rc::ptr_eq(&five, &other_five)); /// ``` + /// + /// [`ptr::eq`]: ../../std/ptr/fn.eq.html pub fn ptr_eq(this: &Self, other: &Self) -> bool { this.ptr.as_ptr() == other.ptr.as_ptr() } @@ -768,12 +773,12 @@ impl<T: Clone> Rc<T> { /// Makes a mutable reference into the given `Rc`. /// - /// If there are other `Rc` pointers to the same value, then `make_mut` will - /// [`clone`] the inner value to ensure unique ownership. This is also + /// If there are other `Rc` pointers to the same allocation, then `make_mut` will + /// [`clone`] the inner value to a new allocation to ensure unique ownership. This is also /// referred to as clone-on-write. /// - /// If there are no other `Rc` pointers to this value, then [`Weak`] - /// pointers to this value will be dissassociated. + /// If there are no other `Rc` pointers to this allocation, then [`Weak`] + /// pointers to this allocation will be disassociated. /// /// See also [`get_mut`], which will fail rather than cloning. /// @@ -794,12 +799,12 @@ /// *Rc::make_mut(&mut data) += 1; // Won't clone anything /// *Rc::make_mut(&mut other_data) *= 2; // Won't clone anything /// - /// // Now `data` and `other_data` point to different values. + /// // Now `data` and `other_data` point to different allocations. /// assert_eq!(*data, 8); /// assert_eq!(*other_data, 12); /// ``` /// - /// [`Weak`] pointers will be dissassociated: + /// [`Weak`] pointers will be disassociated: /// /// ``` /// use std::rc::Rc; @@ -837,7 +842,7 @@ // returned is the *only* pointer that will ever be returned to T. Our // reference count is guaranteed to be 1 at this point, and we required // the `Rc<T>` itself to be `mut`, so we're returning the only possible - // reference to the inner value. + // reference to the allocation. unsafe { &mut this.ptr.as_mut().value } @@ -861,11 +866,9 @@ /// } /// } /// - /// fn main() { - /// let my_string = "Hello World".to_string(); - /// print_if_string(Rc::new(my_string)); - /// print_if_string(Rc::new(0i8)); - /// } + /// let my_string = "Hello World".to_string(); + /// print_if_string(Rc::new(my_string)); + /// print_if_string(Rc::new(0i8)); /// ``` pub fn downcast<T: Any>(self) -> Result<Rc<T>, Rc<dyn Any>> { if (*self).is::<T>() { @@ -880,7 +883,7 @@ impl<T: ?Sized> Rc<T> { /// Allocates an `RcBox<T>` with sufficient space for - /// a possibly-unsized value where the value has the layout provided. + /// a possibly-unsized inner value where the value has the layout provided. /// /// The function `mem_to_rcbox` is called with the data pointer /// and must return back a (potentially fat)-pointer for the `RcBox<T>`. @@ -910,7 +913,7 @@ inner } - /// Allocates an `RcBox<T>` with sufficient space for an unsized value + /// Allocates an `RcBox<T>` with sufficient space for an unsized inner value unsafe fn allocate_for_ptr(ptr: *const T) -> *mut RcBox<T> { // Allocate for the `RcBox<T>` using the given value. Self::allocate_for_layout( @@ -1113,7 +1116,7 @@ impl<T: ?Sized> Clone for Rc<T> { /// Makes a clone of the `Rc` pointer. /// - /// This creates another pointer to the same inner value, increasing the + /// This creates another pointer to the same allocation, increasing the /// strong reference count. /// /// # Examples @@ -1174,6 +1177,8 @@ /// store large values, that are slow to clone, but also heavy to check for equality, causing this /// cost to pay off more easily. It's also more likely to have two `Rc` clones, that point to /// the same value, than two `&T`s. +/// +/// We can only do this when `T: Eq` as a `PartialEq` might be deliberately irreflexive. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized + Eq> RcEqIdent<T> for Rc<T> { #[inline] @@ -1191,9 +1196,11 @@ impl<T: ?Sized + PartialEq> PartialEq for Rc<T> { /// Equality for two `Rc`s. /// - /// Two `Rc`s are equal if their inner values are equal. + /// Two `Rc`s are equal if their inner values are equal, even if they are + /// stored in different allocation. /// - /// If `T` also implements `Eq`, two `Rc`s that point to the same value are + /// If `T` also implements `Eq` (implying reflexivity of equality), + /// two `Rc`s that point to the same allocation are /// always equal. /// /// # Examples @@ -1214,7 +1221,8 @@ /// /// Two `Rc`s are unequal if their inner values are unequal. /// - /// If `T` also implements `Eq`, two `Rc`s that point to the same value are + /// If `T` also implements `Eq` (implying reflexivity of equality), + /// two `Rc`s that point to the same allocation are /// never unequal. /// /// # Examples @@ -1543,17 +1551,18 @@ } /// `Weak` is a version of [`Rc`] that holds a non-owning reference to the -/// managed value. The value is accessed by calling [`upgrade`] on the `Weak` +/// managed allocation. The allocation is accessed by calling [`upgrade`] on the `Weak` /// pointer, which returns an [`Option`]`<`[`Rc`]`<T>>`. /// /// Since a `Weak` reference does not count towards ownership, it will not -/// prevent the inner value from being dropped, and `Weak` itself makes no -/// guarantees about the value still being present and may return [`None`] -/// when [`upgrade`]d. +/// prevent the value stored in the allocation from being dropped, and `Weak` itself makes no +/// guarantees about the value still being present. Thus it may return [`None`] +/// when [`upgrade`]d. Note however that a `Weak` reference *does* prevent the allocation +/// itself (the backing store) from being deallocated. /// -/// A `Weak` pointer is useful for keeping a temporary reference to the value -/// within [`Rc`] without extending its lifetime. It is also used to prevent -/// circular references between [`Rc`] pointers, since mutual owning references +/// A `Weak` pointer is useful for keeping a temporary reference to the allocation +/// managed by [`Rc`] without preventing its inner value from being dropped. It is also used to +/// prevent circular references between [`Rc`] pointers, since mutual owning references /// would never allow either [`Rc`] to be dropped. For example, a tree could /// have strong [`Rc`] pointers from parent nodes to children, and `Weak` /// pointers from children back to their parents. @@ -1752,10 +1761,10 @@ } impl<T: ?Sized> Weak<T> { - /// Attempts to upgrade the `Weak` pointer to an [`Rc`], extending - /// the lifetime of the value if successful. + /// Attempts to upgrade the `Weak` pointer to an [`Rc`], delaying + /// dropping of the inner value if successful. /// - /// Returns [`None`] if the value has since been dropped. + /// Returns [`None`] if the inner value has since been dropped. /// /// [`Rc`]: struct.Rc.html /// [`None`]: ../../std/option/enum.Option.html @@ -1789,7 +1798,7 @@ } } - /// Gets the number of strong (`Rc`) pointers pointing to this value. + /// Gets the number of strong (`Rc`) pointers pointing to this allocation. /// /// If `self` was created using [`Weak::new`], this will return 0. /// @@ -1803,11 +1812,11 @@ } } - /// Gets the number of `Weak` pointers pointing to this value. + /// Gets the number of `Weak` pointers pointing to this allocation. /// /// If `self` was created using [`Weak::new`], this will return `None`. If /// not, the returned value is at least 1, since `self` still points to the - /// value. + /// allocation. /// /// [`Weak::new`]: #method.new #[unstable(feature = "weak_counts", issue = "57977")] @@ -1832,14 +1841,14 @@ } } - /// Returns `true` if the two `Weak`s point to the same value (not just - /// values that compare as equal), or if both don't point to any value + /// Returns `true` if the two `Weak`s point to the same allocation (similar to + /// [`ptr::eq`]), or if both don't point to any allocation /// (because they were created with `Weak::new()`). /// /// # Notes /// /// Since this compares pointers it means that `Weak::new()` will equal each - /// other, even though they don't point to any value. + /// other, even though they don't point to any allocation. /// /// # Examples /// @@ -1871,6 +1880,8 @@ /// let third = Rc::downgrade(&third_rc); /// assert!(!first.ptr_eq(&third)); /// ``` + /// + /// [`ptr::eq`]: ../../std/ptr/fn.eq.html #[inline] #[stable(feature = "weak_ptr_eq", since = "1.39.0")] pub fn ptr_eq(&self, other: &Self) -> bool { @@ -1920,7 +1931,7 @@ #[stable(feature = "rc_weak", since = "1.4.0")] impl<T: ?Sized> Clone for Weak<T> { - /// Makes a clone of the `Weak` pointer that points to the same value. + /// Makes a clone of the `Weak` pointer that points to the same allocation. /// /// # Examples ///
diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 881d499..08243ef 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs
@@ -411,25 +411,16 @@ /// Basic usage: /// /// ``` - /// #![feature(repeat_generic_slice)] - /// - /// fn main() { - /// assert_eq!([1, 2].repeat(3), vec![1, 2, 1, 2, 1, 2]); - /// } + /// assert_eq!([1, 2].repeat(3), vec![1, 2, 1, 2, 1, 2]); /// ``` /// /// A panic upon overflow: /// /// ```should_panic - /// #![feature(repeat_generic_slice)] - /// fn main() { - /// // this will panic at runtime - /// b"0123456789abcdef".repeat(usize::max_value()); - /// } + /// // this will panic at runtime + /// b"0123456789abcdef".repeat(usize::max_value()); /// ``` - #[unstable(feature = "repeat_generic_slice", - reason = "it's on str, why not on slice?", - issue = "48784")] + #[stable(feature = "repeat_generic_slice", since = "1.40.0")] pub fn repeat(&self, n: usize) -> Vec<T> where T: Copy { if n == 0 { return Vec::new();
diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 9a1342c..83816d8 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs
@@ -456,7 +456,7 @@ } } } - return s; + s } /// Converts a [`Box<str>`] into a [`String`] without copying or allocating. @@ -500,10 +500,8 @@ /// A panic upon overflow: /// /// ```should_panic - /// fn main() { - /// // this will panic at runtime - /// "0123456789abcdef".repeat(usize::max_value()); - /// } + /// // this will panic at runtime + /// "0123456789abcdef".repeat(usize::max_value()); /// ``` #[stable(feature = "repeat_str", since = "1.16.0")] pub fn repeat(&self, n: usize) -> String {
diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 1166e7b..d9927c6 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs
@@ -164,10 +164,8 @@ /// /// fn example_func<A: TraitExample>(example_arg: A) {} /// -/// fn main() { -/// let example_string = String::from("example_string"); -/// example_func(&example_string); -/// } +/// let example_string = String::from("example_string"); +/// example_func(&example_string); /// ``` /// /// There are two options that would work instead. The first would be to @@ -198,20 +196,21 @@ /// /// let story = String::from("Once upon a time..."); /// -/// let ptr = story.as_ptr(); +// FIXME Update this when vec_into_raw_parts is stabilized +/// // Prevent automatically dropping the String's data +/// let mut story = mem::ManuallyDrop::new(story); +/// +/// let ptr = story.as_mut_ptr(); /// let len = story.len(); /// let capacity = story.capacity(); /// /// // story has nineteen bytes /// assert_eq!(19, len); /// -/// // Now that we have our parts, we throw the story away. -/// mem::forget(story); -/// /// // We can re-build a String out of ptr, len, and capacity. This is all /// // unsafe because we are responsible for making sure the components are /// // valid: -/// let s = unsafe { String::from_raw_parts(ptr as *mut _, len, capacity) } ; +/// let s = unsafe { String::from_raw_parts(ptr, len, capacity) } ; /// /// assert_eq!(String::from("Once upon a time..."), s); /// ``` @@ -369,7 +368,6 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_string_new"))] pub const fn new() -> String { String { vec: Vec::new() } } @@ -429,7 +427,7 @@ /// Converts a vector of bytes to a `String`. /// - /// A string slice ([`&str`]) is made of bytes ([`u8`]), and a vector of bytes + /// A string ([`String`]) is made of bytes ([`u8`]), and a vector of bytes /// ([`Vec<u8>`]) is made of bytes, so this function converts between the /// two. Not all byte slices are valid `String`s, however: `String` /// requires that it is valid UTF-8. `from_utf8()` checks to ensure that @@ -446,7 +444,7 @@ /// If you need a [`&str`] instead of a `String`, consider /// [`str::from_utf8`]. /// - /// The inverse of this method is [`as_bytes`]. + /// The inverse of this method is [`into_bytes`]. /// /// # Errors /// @@ -480,11 +478,11 @@ /// with this error. /// /// [`from_utf8_unchecked`]: struct.String.html#method.from_utf8_unchecked - /// [`&str`]: ../../std/primitive.str.html + /// [`String`]: struct.String.html /// [`u8`]: ../../std/primitive.u8.html /// [`Vec<u8>`]: ../../std/vec/struct.Vec.html /// [`str::from_utf8`]: ../../std/str/fn.from_utf8.html - /// [`as_bytes`]: struct.String.html#method.as_bytes + /// [`into_bytes`]: struct.String.html#method.into_bytes /// [`FromUtf8Error`]: struct.FromUtf8Error.html /// [`Err`]: ../../std/result/enum.Result.html#variant.Err #[inline] @@ -650,6 +648,37 @@ decode_utf16(v.iter().cloned()).map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)).collect() } + /// Decomposes a `String` into its raw components. + /// + /// Returns the raw pointer to the underlying data, the length of + /// the string (in bytes), and the allocated capacity of the data + /// (in bytes). These are the same arguments in the same order as + /// the arguments to [`from_raw_parts`]. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `String`. The only way to do + /// this is to convert the raw pointer, length, and capacity back + /// into a `String` with the [`from_raw_parts`] function, allowing + /// the destructor to perform the cleanup. + /// + /// [`from_raw_parts`]: #method.from_raw_parts + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_into_raw_parts)] + /// let s = String::from("hello"); + /// + /// let (ptr, len, cap) = s.into_raw_parts(); + /// + /// let rebuilt = unsafe { String::from_raw_parts(ptr, len, cap) }; + /// assert_eq!(rebuilt, "hello"); + /// ``` + #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] + pub fn into_raw_parts(self) -> (*mut u8, usize, usize) { + self.vec.into_raw_parts() + } + /// Creates a new `String` from a length, capacity, and pointer. /// /// # Safety @@ -680,13 +709,16 @@ /// /// unsafe { /// let s = String::from("hello"); - /// let ptr = s.as_ptr(); + /// + // FIXME Update this when vec_into_raw_parts is stabilized + /// // Prevent automatically dropping the String's data + /// let mut s = mem::ManuallyDrop::new(s); + /// + /// let ptr = s.as_mut_ptr(); /// let len = s.len(); /// let capacity = s.capacity(); /// - /// mem::forget(s); - /// - /// let s = String::from_raw_parts(ptr as *mut _, len, capacity); + /// let s = String::from_raw_parts(ptr, len, capacity); /// /// assert_eq!(String::from("hello"), s); /// }
diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 45f9816..80d6c6e 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs
@@ -45,10 +45,10 @@ /// /// The type `Arc<T>` provides shared ownership of a value of type `T`, /// allocated in the heap. Invoking [`clone`][clone] on `Arc` produces -/// a new `Arc` instance, which points to the same value on the heap as the +/// a new `Arc` instance, which points to the same allocation on the heap as the /// source `Arc`, while increasing a reference count. When the last `Arc` -/// pointer to a given value is destroyed, the pointed-to value is also -/// destroyed. +/// pointer to a given allocation is destroyed, the value stored in that allocation (often +/// referred to as "inner value") is also dropped. /// /// Shared references in Rust disallow mutation by default, and `Arc` is no /// exception: you cannot generally obtain a mutable reference to something @@ -61,7 +61,7 @@ /// Unlike [`Rc<T>`], `Arc<T>` uses atomic operations for its reference /// counting. This means that it is thread-safe. The disadvantage is that /// atomic operations are more expensive than ordinary memory accesses. If you -/// are not sharing reference-counted values between threads, consider using +/// are not sharing reference-counted allocations between threads, consider using /// [`Rc<T>`] for lower overhead. [`Rc<T>`] is a safe default, because the /// compiler will catch any attempt to send an [`Rc<T>`] between threads. /// However, a library might choose `Arc<T>` in order to give library consumers @@ -85,8 +85,10 @@ /// /// The [`downgrade`][downgrade] method can be used to create a non-owning /// [`Weak`][weak] pointer. A [`Weak`][weak] pointer can be [`upgrade`][upgrade]d -/// to an `Arc`, but this will return [`None`] if the value has already been -/// dropped. +/// to an `Arc`, but this will return [`None`] if the value stored in the allocation has +/// already been dropped. In other words, `Weak` pointers do not keep the value +/// inside the allocation alive; however, they *do* keep the allocation +/// (the backing store for the value) alive. /// /// A cycle between `Arc` pointers will never be deallocated. For this reason, /// [`Weak`][weak] is used to break cycles. For example, a tree could have @@ -121,8 +123,8 @@ /// Arc::downgrade(&my_arc); /// ``` /// -/// [`Weak<T>`][weak] does not auto-dereference to `T`, because the value may have -/// already been destroyed. +/// [`Weak<T>`][weak] does not auto-dereference to `T`, because the inner value may have +/// already been dropped. /// /// [arc]: struct.Arc.html /// [weak]: struct.Weak.html @@ -221,17 +223,18 @@ } /// `Weak` is a version of [`Arc`] that holds a non-owning reference to the -/// managed value. The value is accessed by calling [`upgrade`] on the `Weak` +/// managed allocation. The allocation is accessed by calling [`upgrade`] on the `Weak` /// pointer, which returns an [`Option`]`<`[`Arc`]`<T>>`. /// /// Since a `Weak` reference does not count towards ownership, it will not -/// prevent the inner value from being dropped, and `Weak` itself makes no -/// guarantees about the value still being present and may return [`None`] -/// when [`upgrade`]d. +/// prevent the value stored in the allocation from being dropped, and `Weak` itself makes no +/// guarantees about the value still being present. Thus it may return [`None`] +/// when [`upgrade`]d. Note however that a `Weak` reference *does* prevent the allocation +/// itself (the backing store) from being deallocated. /// -/// A `Weak` pointer is useful for keeping a temporary reference to the value -/// within [`Arc`] without extending its lifetime. It is also used to prevent -/// circular references between [`Arc`] pointers, since mutual owning references +/// A `Weak` pointer is useful for keeping a temporary reference to the allocation +/// managed by [`Arc`] without preventing its inner value from being dropped. It is also used to +/// prevent circular references between [`Arc`] pointers, since mutual owning references /// would never allow either [`Arc`] to be dropped. For example, a tree could /// have strong [`Arc`] pointers from parent nodes to children, and `Weak` /// pointers from children back to their parents. @@ -345,7 +348,7 @@ unsafe { Pin::new_unchecked(Arc::new(data)) } } - /// Returns the contained value, if the `Arc` has exactly one strong reference. + /// Returns the inner value, if the `Arc` has exactly one strong reference. /// /// Otherwise, an [`Err`][result] is returned with the same `Arc` that was /// passed in. @@ -426,7 +429,7 @@ /// # Safety /// /// As with [`MaybeUninit::assume_init`], - /// it is up to the caller to guarantee that the value + /// it is up to the caller to guarantee that the inner value /// really is in an initialized state. /// Calling this when the content is not yet fully initialized /// causes immediate undefined behavior. @@ -465,7 +468,7 @@ /// # Safety /// /// As with [`MaybeUninit::assume_init`], - /// it is up to the caller to guarantee that the value + /// it is up to the caller to guarantee that the inner value /// really is in an initialized state. /// Calling this when the content is not yet fully initialized /// causes immediate undefined behavior. @@ -584,7 +587,7 @@ unsafe { NonNull::new_unchecked(Arc::into_raw(this) as *mut _) } } - /// Creates a new [`Weak`][weak] pointer to this value. + /// Creates a new [`Weak`][weak] pointer to this allocation. /// /// [weak]: struct.Weak.html /// @@ -628,7 +631,7 @@ } } - /// Gets the number of [`Weak`][weak] pointers to this value. + /// Gets the number of [`Weak`][weak] pointers to this allocation. /// /// [weak]: struct.Weak.html /// @@ -659,7 +662,7 @@ if cnt == usize::MAX { 0 } else { cnt - 1 } } - /// Gets the number of strong (`Arc`) pointers to this value. + /// Gets the number of strong (`Arc`) pointers to this allocation. /// /// # Safety /// @@ -710,8 +713,8 @@ #[inline] #[stable(feature = "ptr_eq", since = "1.17.0")] - /// Returns `true` if the two `Arc`s point to the same value (not - /// just values that compare as equal). + /// Returns `true` if the two `Arc`s point to the same allocation + /// (in a vein similar to [`ptr::eq`]). /// /// # Examples /// @@ -725,6 +728,8 @@ /// assert!(Arc::ptr_eq(&five, &same_five)); /// assert!(!Arc::ptr_eq(&five, &other_five)); /// ``` + /// + /// [`ptr::eq`]: ../../std/ptr/fn.eq.html pub fn ptr_eq(this: &Self, other: &Self) -> bool { this.ptr.as_ptr() == other.ptr.as_ptr() } @@ -732,7 +737,7 @@ impl<T: ?Sized> Arc<T> { /// Allocates an `ArcInner<T>` with sufficient space for - /// a possibly-unsized value where the value has the layout provided. + /// a possibly-unsized inner value where the value has the layout provided. /// /// The function `mem_to_arcinner` is called with the data pointer /// and must return back a (potentially fat)-pointer for the `ArcInner<T>`. @@ -761,7 +766,7 @@ inner } - /// Allocates an `ArcInner<T>` with sufficient space for an unsized value. + /// Allocates an `ArcInner<T>` with sufficient space for an unsized inner value. unsafe fn allocate_for_ptr(ptr: *const T) -> *mut ArcInner<T> { // Allocate for the `ArcInner<T>` using the given value. Self::allocate_for_layout( @@ -903,7 +908,7 @@ impl<T: ?Sized> Clone for Arc<T> { /// Makes a clone of the `Arc` pointer. /// - /// This creates another pointer to the same inner value, increasing the + /// This creates another pointer to the same allocation, increasing the /// strong reference count. /// /// # Examples @@ -965,15 +970,19 @@ impl<T: Clone> Arc<T> { /// Makes a mutable reference into the given `Arc`. /// - /// If there are other `Arc` or [`Weak`][weak] pointers to the same value, - /// then `make_mut` will invoke [`clone`][clone] on the inner value to - /// ensure unique ownership. This is also referred to as clone-on-write. + /// If there are other `Arc` or [`Weak`][weak] pointers to the same allocation, + /// then `make_mut` will create a new allocation and invoke [`clone`][clone] on the inner value + /// to ensure unique ownership. This is also referred to as clone-on-write. + /// + /// Note that this differs from the behavior of [`Rc::make_mut`] which disassociates + /// any remaining `Weak` pointers. /// /// See also [`get_mut`][get_mut], which will fail rather than cloning. /// /// [weak]: struct.Weak.html /// [clone]: ../../std/clone/trait.Clone.html#tymethod.clone /// [get_mut]: struct.Arc.html#method.get_mut + /// [`Rc::make_mut`]: ../rc/struct.Rc.html#method.make_mut /// /// # Examples /// @@ -988,7 +997,7 @@ /// *Arc::make_mut(&mut data) += 1; // Won't clone anything /// *Arc::make_mut(&mut other_data) *= 2; // Won't clone anything /// - /// // Now `data` and `other_data` point to different values. + /// // Now `data` and `other_data` point to different allocations. /// assert_eq!(*data, 8); /// assert_eq!(*other_data, 12); /// ``` @@ -1048,14 +1057,14 @@ } impl<T: ?Sized> Arc<T> { - /// Returns a mutable reference to the inner value, if there are - /// no other `Arc` or [`Weak`][weak] pointers to the same value. + /// Returns a mutable reference into the given `Arc`, if there are + /// no other `Arc` or [`Weak`][weak] pointers to the same allocation. /// /// Returns [`None`][option] otherwise, because it is not safe to /// mutate a shared value. /// /// See also [`make_mut`][make_mut], which will [`clone`][clone] - /// the inner value when it's shared. + /// the inner value when there are other pointers. /// /// [weak]: struct.Weak.html /// [option]: ../../std/option/enum.Option.html @@ -1091,7 +1100,7 @@ } } - /// Returns a mutable reference to the inner value, + /// Returns a mutable reference into the given `Arc`, /// without any check. /// /// See also [`get_mut`], which is safe and does appropriate checks. @@ -1100,7 +1109,7 @@ /// /// # Safety /// - /// Any other `Arc` or [`Weak`] pointers to the same value must not be dereferenced + /// Any other `Arc` or [`Weak`] pointers to the same allocation must not be dereferenced /// for the duration of the returned borrow. /// This is trivially the case if no such pointers exist, /// for example immediately after `Arc::new`. @@ -1244,11 +1253,9 @@ /// } /// } /// - /// fn main() { - /// let my_string = "Hello World".to_string(); - /// print_if_string(Arc::new(my_string)); - /// print_if_string(Arc::new(0i8)); - /// } + /// let my_string = "Hello World".to_string(); + /// print_if_string(Arc::new(my_string)); + /// print_if_string(Arc::new(0i8)); /// ``` pub fn downcast<T>(self) -> Result<Arc<T>, Self> where @@ -1426,10 +1433,10 @@ } impl<T: ?Sized> Weak<T> { - /// Attempts to upgrade the `Weak` pointer to an [`Arc`], extending - /// the lifetime of the value if successful. + /// Attempts to upgrade the `Weak` pointer to an [`Arc`], delaying + /// dropping of the inner value if successful. /// - /// Returns [`None`] if the value has since been dropped. + /// Returns [`None`] if the inner value has since been dropped. /// /// [`Arc`]: struct.Arc.html /// [`None`]: ../../std/option/enum.Option.html#variant.None @@ -1484,7 +1491,7 @@ } } - /// Gets the number of strong (`Arc`) pointers pointing to this value. + /// Gets the number of strong (`Arc`) pointers pointing to this allocation. /// /// If `self` was created using [`Weak::new`], this will return 0. /// @@ -1499,17 +1506,17 @@ } /// Gets an approximation of the number of `Weak` pointers pointing to this - /// value. + /// allocation. /// /// If `self` was created using [`Weak::new`], this will return 0. If not, /// the returned value is at least 1, since `self` still points to the - /// value. + /// allocation. /// /// # Accuracy /// /// Due to implementation details, the returned value can be off by 1 in /// either direction when other threads are manipulating any `Arc`s or - /// `Weak`s pointing to the same value. + /// `Weak`s pointing to the same allocation. /// /// [`Weak::new`]: #method.new #[unstable(feature = "weak_counts", issue = "57977")] @@ -1550,14 +1557,14 @@ } } - /// Returns `true` if the two `Weak`s point to the same value (not just - /// values that compare as equal), or if both don't point to any value + /// Returns `true` if the two `Weak`s point to the same allocation (similar to + /// [`ptr::eq`]), or if both don't point to any allocation /// (because they were created with `Weak::new()`). /// /// # Notes /// /// Since this compares pointers it means that `Weak::new()` will equal each - /// other, even though they don't point to any value. + /// other, even though they don't point to any allocation. /// /// # Examples /// @@ -1589,6 +1596,8 @@ /// let third = Arc::downgrade(&third_rc); /// assert!(!first.ptr_eq(&third)); /// ``` + /// + /// [`ptr::eq`]: ../../std/ptr/fn.eq.html #[inline] #[stable(feature = "weak_ptr_eq", since = "1.39.0")] pub fn ptr_eq(&self, other: &Self) -> bool { @@ -1598,7 +1607,7 @@ #[stable(feature = "arc_weak", since = "1.4.0")] impl<T: ?Sized> Clone for Weak<T> { - /// Makes a clone of the `Weak` pointer that points to the same value. + /// Makes a clone of the `Weak` pointer that points to the same allocation. /// /// # Examples /// @@ -1629,7 +1638,7 @@ } } - return Weak { ptr: self.ptr }; + Weak { ptr: self.ptr } } } @@ -1728,6 +1737,8 @@ /// store large values, that are slow to clone, but also heavy to check for equality, causing this /// cost to pay off more easily. It's also more likely to have two `Arc` clones, that point to /// the same value, than two `&T`s. +/// +/// We can only do this when `T: Eq` as a `PartialEq` might be deliberately irreflexive. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized + Eq> ArcEqIdent<T> for Arc<T> { #[inline] @@ -1745,10 +1756,11 @@ impl<T: ?Sized + PartialEq> PartialEq for Arc<T> { /// Equality for two `Arc`s. /// - /// Two `Arc`s are equal if their inner values are equal. + /// Two `Arc`s are equal if their inner values are equal, even if they are + /// stored in different allocation. /// - /// If `T` also implements `Eq`, two `Arc`s that point to the same value are - /// always equal. + /// If `T` also implements `Eq` (implying reflexivity of equality), + /// two `Arc`s that point to the same allocation are always equal. /// /// # Examples /// @@ -1768,8 +1780,8 @@ /// /// Two `Arc`s are unequal if their inner values are unequal. /// - /// If `T` also implements `Eq`, two `Arc`s that point to the same value are - /// never unequal. + /// If `T` also implements `Eq` (implying reflexivity of equality), + /// two `Arc`s that point to the same value are never unequal. /// /// # Examples ///
diff --git a/src/liballoc/tests/binary_heap.rs b/src/liballoc/tests/binary_heap.rs index 0685fa9..a44cf1e 100644 --- a/src/liballoc/tests/binary_heap.rs +++ b/src/liballoc/tests/binary_heap.rs
@@ -1,10 +1,6 @@ -use std::cmp; use std::collections::BinaryHeap; use std::collections::binary_heap::{Drain, PeekMut}; -use std::panic::{self, AssertUnwindSafe}; -use std::sync::atomic::{AtomicUsize, Ordering}; - -use rand::{thread_rng, seq::SliceRandom}; +use std::iter::TrustedLen; #[test] fn test_iterator() { @@ -19,7 +15,7 @@ } #[test] -fn test_iterator_reverse() { +fn test_iter_rev_cloned_collect() { let data = vec![5, 9, 3]; let iterout = vec![3, 5, 9]; let pq = BinaryHeap::from(data); @@ -29,7 +25,7 @@ } #[test] -fn test_move_iter() { +fn test_into_iter_collect() { let data = vec![5, 9, 3]; let iterout = vec![9, 5, 3]; let pq = BinaryHeap::from(data); @@ -39,7 +35,7 @@ } #[test] -fn test_move_iter_size_hint() { +fn test_into_iter_size_hint() { let data = vec![5, 9]; let pq = BinaryHeap::from(data); @@ -56,7 +52,7 @@ } #[test] -fn test_move_iter_reverse() { +fn test_into_iter_rev_collect() { let data = vec![5, 9, 3]; let iterout = vec![3, 5, 9]; let pq = BinaryHeap::from(data); @@ -66,6 +62,65 @@ } #[test] +fn test_into_iter_sorted_collect() { + let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); + let it = heap.into_iter_sorted(); + let sorted = it.collect::<Vec<_>>(); + assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]); +} + +#[test] +fn test_drain_sorted_collect() { + let mut heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); + let it = heap.drain_sorted(); + let sorted = it.collect::<Vec<_>>(); + assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]); +} + +fn check_exact_size_iterator<I: ExactSizeIterator>(len: usize, it: I) { + let mut it = it; + + for i in 0..it.len() { + let (lower, upper) = it.size_hint(); + assert_eq!(Some(lower), upper); + assert_eq!(lower, len - i); + assert_eq!(it.len(), len - i); + it.next(); + } + assert_eq!(it.len(), 0); + assert!(it.is_empty()); +} + +#[test] +fn test_exact_size_iterator() { + let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); + check_exact_size_iterator(heap.len(), heap.iter()); + check_exact_size_iterator(heap.len(), heap.clone().into_iter()); + check_exact_size_iterator(heap.len(), heap.clone().into_iter_sorted()); + check_exact_size_iterator(heap.len(), heap.clone().drain()); + check_exact_size_iterator(heap.len(), heap.clone().drain_sorted()); +} + +fn check_trusted_len<I: TrustedLen>(len: usize, it: I) { + let mut it = it; + for i in 0..len { + let (lower, upper) = it.size_hint(); + if upper.is_some() { + assert_eq!(Some(lower), upper); + assert_eq!(lower, len - i); + } + it.next(); + } +} + +#[test] +fn test_trusted_len() { + let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); + check_trusted_len(heap.len(), heap.clone().into_iter_sorted()); + check_trusted_len(heap.len(), heap.clone().drain_sorted()); +} + +#[test] fn test_peek_and_pop() { let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; let mut sorted = data.clone(); @@ -212,6 +267,15 @@ } #[test] +fn test_drain_sorted() { + let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect(); + + assert_eq!(q.drain_sorted().take(5).collect::<Vec<_>>(), vec![9, 8, 7, 6, 5]); + + assert!(q.is_empty()); +} + +#[test] fn test_extend_ref() { let mut a = BinaryHeap::new(); a.push(1); @@ -281,9 +345,15 @@ // even if the order may not be correct. // // Destructors must be called exactly once per element. +// FIXME: re-enable emscripten once it can unwind again #[test] -#[cfg(not(miri))] // Miri does not support catching panics +#[cfg(not(any(miri, target_os = "emscripten")))] // Miri does not support catching panics fn panic_safe() { + use std::cmp; + use std::panic::{self, AssertUnwindSafe}; + use std::sync::atomic::{AtomicUsize, Ordering}; + use rand::{thread_rng, seq::SliceRandom}; + static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0); #[derive(Eq, PartialEq, Ord, Clone, Debug)]
diff --git a/src/liballoc/tests/boxed.rs b/src/liballoc/tests/boxed.rs new file mode 100644 index 0000000..bc3d53b --- /dev/null +++ b/src/liballoc/tests/boxed.rs
@@ -0,0 +1,18 @@ +use std::ptr::NonNull; +use std::mem::MaybeUninit; + +#[test] +fn unitialized_zero_size_box() { + assert_eq!( + &*Box::<()>::new_uninit() as *const _, + NonNull::<MaybeUninit<()>>::dangling().as_ptr(), + ); + assert_eq!( + Box::<[()]>::new_uninit_slice(4).as_ptr(), + NonNull::<MaybeUninit<()>>::dangling().as_ptr(), + ); + assert_eq!( + Box::<[String]>::new_uninit_slice(0).as_ptr(), + NonNull::<MaybeUninit<String>>::dangling().as_ptr(), + ); +}
diff --git a/src/liballoc/tests/btree/set.rs b/src/liballoc/tests/btree/set.rs index 35db18c..e4883ab 100644 --- a/src/liballoc/tests/btree/set.rs +++ b/src/liballoc/tests/btree/set.rs
@@ -48,7 +48,9 @@ f(&set_a, &set_b, &mut |&x| { - assert_eq!(x, expected[i]); + if i < expected.len() { + assert_eq!(x, expected[i]); + } i += 1; true }); @@ -74,20 +76,20 @@ return; } - let large = (0..1000).collect::<Vec<_>>(); + let large = (0..100).collect::<Vec<_>>(); check_intersection(&[], &large, &[]); check_intersection(&large, &[], &[]); check_intersection(&[-1], &large, &[]); check_intersection(&large, &[-1], &[]); check_intersection(&[0], &large, &[0]); check_intersection(&large, &[0], &[0]); - check_intersection(&[999], &large, &[999]); - check_intersection(&large, &[999], &[999]); - check_intersection(&[1000], &large, &[]); - check_intersection(&large, &[1000], &[]); - check_intersection(&[11, 5000, 1, 3, 77, 8924, 103], + check_intersection(&[99], &large, &[99]); + check_intersection(&large, &[99], &[99]); + check_intersection(&[100], &large, &[]); + check_intersection(&large, &[100], &[]); + check_intersection(&[11, 5000, 1, 3, 77, 8924], &large, - &[1, 3, 11, 77, 103]); + &[1, 3, 11, 77]); } #[test] @@ -95,10 +97,15 @@ let x: BTreeSet<i32> = [3, 4].iter().copied().collect(); let y: BTreeSet<i32> = [1, 2, 3].iter().copied().collect(); let mut iter = x.intersection(&y); - assert_eq!(iter.size_hint(), (0, Some(2))); + assert_eq!(iter.size_hint(), (1, Some(1))); assert_eq!(iter.next(), Some(&3)); assert_eq!(iter.size_hint(), (0, Some(0))); assert_eq!(iter.next(), None); + + iter = y.intersection(&y); + assert_eq!(iter.size_hint(), (0, Some(3))); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.size_hint(), (0, Some(2))); } #[test] @@ -111,6 +118,9 @@ check_difference(&[1, 12], &[], &[1, 12]); check_difference(&[], &[1, 2, 3, 9], &[]); check_difference(&[1, 3, 5, 9, 11], &[3, 9], &[1, 5, 11]); + check_difference(&[1, 3, 5, 9, 11], &[3, 6, 9], &[1, 5, 11]); + check_difference(&[1, 3, 5, 9, 11], &[0, 1], &[3, 5, 9, 11]); + check_difference(&[1, 3, 5, 9, 11], &[11, 12], &[1, 3, 5, 9]); check_difference(&[-5, 11, 22, 33, 40, 42], &[-12, -5, 14, 23, 34, 38, 39, 50], &[11, 22, 33, 40, 42]); @@ -119,18 +129,82 @@ return; } - let large = (0..1000).collect::<Vec<_>>(); + let large = (0..100).collect::<Vec<_>>(); check_difference(&[], &large, &[]); check_difference(&[-1], &large, &[-1]); check_difference(&[0], &large, &[]); - check_difference(&[999], &large, &[]); - check_difference(&[1000], &large, &[1000]); - check_difference(&[11, 5000, 1, 3, 77, 8924, 103], + check_difference(&[99], &large, &[]); + check_difference(&[100], &large, &[100]); + check_difference(&[11, 5000, 1, 3, 77, 8924], &large, &[5000, 8924]); check_difference(&large, &[], &large); check_difference(&large, &[-1], &large); - check_difference(&large, &[1000], &large); + check_difference(&large, &[100], &large); +} + +#[test] +fn test_difference_size_hint() { + let s246: BTreeSet<i32> = [2, 4, 6].iter().copied().collect(); + let s23456: BTreeSet<i32> = (2..=6).collect(); + let mut iter = s246.difference(&s23456); + assert_eq!(iter.size_hint(), (0, Some(3))); + assert_eq!(iter.next(), None); + + let s12345: BTreeSet<i32> = (1..=5).collect(); + iter = s246.difference(&s12345); + assert_eq!(iter.size_hint(), (0, Some(3))); + assert_eq!(iter.next(), Some(&6)); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + + let s34567: BTreeSet<i32> = (3..=7).collect(); + iter = s246.difference(&s34567); + assert_eq!(iter.size_hint(), (0, Some(3))); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.size_hint(), (0, Some(2))); + assert_eq!(iter.next(), None); + + let s1: BTreeSet<i32> = (-9..=1).collect(); + iter = s246.difference(&s1); + assert_eq!(iter.size_hint(), (3, Some(3))); + + let s2: BTreeSet<i32> = (-9..=2).collect(); + iter = s246.difference(&s2); + assert_eq!(iter.size_hint(), (2, Some(2))); + assert_eq!(iter.next(), Some(&4)); + assert_eq!(iter.size_hint(), (1, Some(1))); + + let s23: BTreeSet<i32> = (2..=3).collect(); + iter = s246.difference(&s23); + assert_eq!(iter.size_hint(), (1, Some(3))); + assert_eq!(iter.next(), Some(&4)); + assert_eq!(iter.size_hint(), (1, Some(1))); + + let s4: BTreeSet<i32> = (4..=4).collect(); + iter = s246.difference(&s4); + assert_eq!(iter.size_hint(), (2, Some(3))); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.size_hint(), (1, Some(2))); + assert_eq!(iter.next(), Some(&6)); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + + let s56: BTreeSet<i32> = (5..=6).collect(); + iter = s246.difference(&s56); + assert_eq!(iter.size_hint(), (1, Some(3))); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.size_hint(), (0, Some(2))); + + let s6: BTreeSet<i32> = (6..=19).collect(); + iter = s246.difference(&s6); + assert_eq!(iter.size_hint(), (2, Some(2))); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.size_hint(), (1, Some(1))); + + let s7: BTreeSet<i32> = (7..=19).collect(); + iter = s246.difference(&s7); + assert_eq!(iter.size_hint(), (3, Some(3))); } #[test] @@ -148,6 +222,18 @@ } #[test] +fn test_symmetric_difference_size_hint() { + let x: BTreeSet<i32> = [2, 4].iter().copied().collect(); + let y: BTreeSet<i32> = [1, 2, 3].iter().copied().collect(); + let mut iter = x.symmetric_difference(&y); + assert_eq!(iter.size_hint(), (0, Some(5))); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.size_hint(), (0, Some(4))); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.size_hint(), (0, Some(1))); +} + +#[test] fn test_union() { fn check_union(a: &[i32], b: &[i32], expected: &[i32]) { check(a, b, expected, |x, y, f| x.union(y).all(f)) @@ -162,6 +248,18 @@ } #[test] +fn test_union_size_hint() { + let x: BTreeSet<i32> = [2, 4].iter().copied().collect(); + let y: BTreeSet<i32> = [1, 2, 3].iter().copied().collect(); + let mut iter = x.union(&y); + assert_eq!(iter.size_hint(), (3, Some(5))); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.size_hint(), (2, Some(4))); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.size_hint(), (1, Some(2))); +} + +#[test] // Only tests the simple function definition with respect to intersection fn test_is_disjoint() { let one = [1].iter().collect::<BTreeSet<_>>(); @@ -170,7 +268,7 @@ } #[test] -// Also tests the trivial function definition of is_superset +// Also implicitly tests the trivial function definition of is_superset fn test_is_subset() { fn is_subset(a: &[i32], b: &[i32]) -> bool { let set_a = a.iter().collect::<BTreeSet<_>>(); @@ -188,23 +286,23 @@ assert_eq!(is_subset(&[1, 2], &[1, 2]), true); assert_eq!(is_subset(&[1, 2], &[2, 3]), false); assert_eq!(is_subset(&[-5, 11, 22, 33, 40, 42], - &[-12, -5, 14, 23, 11, 34, 22, 38, 33, 42, 39, 40]), + &[-12, -5, 11, 14, 22, 23, 33, 34, 38, 39, 40, 42]), true); assert_eq!(is_subset(&[-5, 11, 22, 33, 40, 42], - &[-12, -5, 14, 23, 34, 38, 22, 11]), + &[-12, -5, 11, 14, 22, 23, 34, 38]), false); if cfg!(miri) { // Miri is too slow return; } - let large = (0..1000).collect::<Vec<_>>(); + let large = (0..100).collect::<Vec<_>>(); assert_eq!(is_subset(&[], &large), true); assert_eq!(is_subset(&large, &[]), false); assert_eq!(is_subset(&[-1], &large), false); assert_eq!(is_subset(&[0], &large), true); assert_eq!(is_subset(&[1, 2], &large), true); - assert_eq!(is_subset(&[999, 1000], &large), false); + assert_eq!(is_subset(&[99, 100], &large), false); } #[test]
diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 5723a30..3273feb 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs
@@ -2,19 +2,21 @@ #![feature(box_syntax)] #![feature(drain_filter)] #![feature(exact_size_is_empty)] -#![feature(option_flattening)] +#![feature(new_uninit)] #![feature(pattern)] -#![feature(repeat_generic_slice)] #![feature(trusted_len)] #![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(associated_type_bounds)] +#![feature(binary_heap_into_iter_sorted)] +#![feature(binary_heap_drain_sorted)] use std::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; mod arc; mod binary_heap; +mod boxed; mod btree; mod cow_str; mod fmt;
diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 4332b2e..cb73c7c 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs
@@ -483,7 +483,7 @@ } #[test] - #[cfg(not(target_arch = "asmjs"))] // hits an OOM + #[cfg(not(target_os = "emscripten"))] // hits an OOM #[cfg(not(miri))] // Miri is too slow fn simple_big() { fn a_million_letter_x() -> String {
diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index 29a22aa..8053721 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs
@@ -944,8 +944,10 @@ } } +// Miri does not support catching panics +// FIXME: re-enable emscripten once it can unwind again #[test] -#[cfg(not(miri))] // Miri does not support catching panics +#[cfg(not(any(miri, target_os = "emscripten")))] fn drain_filter_consumed_panic() { use std::rc::Rc; use std::sync::Mutex; @@ -995,8 +997,9 @@ } } +// FIXME: Re-enable emscripten once it can catch panics #[test] -#[cfg(not(miri))] // Miri does not support catching panics +#[cfg(not(any(miri, target_os = "emscripten")))] // Miri does not support catching panics fn drain_filter_unconsumed_panic() { use std::rc::Rc; use std::sync::Mutex; @@ -1281,3 +1284,51 @@ v.pop().unwrap(); assert_eq!(*v0, 13); } + +// https://github.com/rust-lang/rust/pull/49496 introduced specialization based on: +// +// ``` +// unsafe impl<T: ?Sized> IsZero for *mut T { +// fn is_zero(&self) -> bool { +// (*self).is_null() +// } +// } +// ``` +// +// … to call `RawVec::with_capacity_zeroed` for creating `Vec<*mut T>`, +// which is incorrect for fat pointers since `<*mut T>::is_null` only looks at the data component. +// That is, a fat pointer can be “null” without being made entirely of zero bits. +#[test] +fn vec_macro_repeating_null_raw_fat_pointer() { + let raw_dyn = &mut (|| ()) as &mut dyn Fn() as *mut dyn Fn(); + let vtable = dbg!(ptr_metadata(raw_dyn)); + let null_raw_dyn = ptr_from_raw_parts(std::ptr::null_mut(), vtable); + assert!(null_raw_dyn.is_null()); + + let vec = vec![null_raw_dyn; 1]; + dbg!(ptr_metadata(vec[0])); + assert!(vec[0] == null_raw_dyn); + + // Polyfill for https://github.com/rust-lang/rfcs/pull/2580 + + fn ptr_metadata(ptr: *mut dyn Fn()) -> *mut () { + unsafe { + std::mem::transmute::<*mut dyn Fn(), DynRepr>(ptr).vtable + } + } + + fn ptr_from_raw_parts(data: *mut (), vtable: *mut()) -> *mut dyn Fn() { + unsafe { + std::mem::transmute::<DynRepr, *mut dyn Fn()>(DynRepr { + data, + vtable + }) + } + } + + #[repr(C)] + struct DynRepr { + data: *mut (), + vtable: *mut (), + } +}
diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 405969a..5b53a6a 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs
@@ -154,8 +154,8 @@ /// println!("{}", v[6]); // it will panic! /// ``` /// -/// In conclusion: always check if the index you want to get really exists -/// before doing it. +/// Use [`get`] and [`get_mut`] if you want to check whether the index is in +/// the `Vec`. /// /// # Slicing /// @@ -277,6 +277,8 @@ /// The order has changed in the past and may change again. /// /// [`vec!`]: ../../std/macro.vec.html +/// [`get`]: ../../std/vec/struct.Vec.html#method.get +/// [`get_mut`]: ../../std/vec/struct.Vec.html#method.get_mut /// [`Index`]: ../../std/ops/trait.Index.html /// [`String`]: ../../std/string/struct.String.html /// [`&str`]: ../../std/primitive.str.html @@ -291,7 +293,7 @@ /// [`reserve`]: ../../std/vec/struct.Vec.html#method.reserve /// [owned slice]: ../../std/boxed/struct.Box.html #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(all(not(bootstrap), not(test)), rustc_diagnostic_item = "vec_type")] +#[cfg_attr(not(test), rustc_diagnostic_item = "vec_type")] pub struct Vec<T> { buf: RawVec<T>, len: usize, @@ -314,7 +316,6 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_vec_new"))] pub const fn new() -> Vec<T> { Vec { buf: RawVec::NEW, @@ -359,6 +360,44 @@ } } + /// Decomposes a `Vec<T>` into its raw components. + /// + /// Returns the raw pointer to the underlying data, the length of + /// the vector (in elements), and the allocated capacity of the + /// data (in elements). These are the same arguments in the same + /// order as the arguments to [`from_raw_parts`]. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `Vec`. The only way to do + /// this is to convert the raw pointer, length, and capacity back + /// into a `Vec` with the [`from_raw_parts`] function, allowing + /// the destructor to perform the cleanup. + /// + /// [`from_raw_parts`]: #method.from_raw_parts + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_into_raw_parts)] + /// let v: Vec<i32> = vec![-1, 0, 1]; + /// + /// let (ptr, len, cap) = v.into_raw_parts(); + /// + /// let rebuilt = unsafe { + /// // We can now make changes to the components, such as + /// // transmuting the raw pointer to a compatible type. + /// let ptr = ptr as *mut u32; + /// + /// Vec::from_raw_parts(ptr, len, cap) + /// }; + /// assert_eq!(rebuilt, [4294967295, 0, 1]); + /// ``` + #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] + pub fn into_raw_parts(self) -> (*mut T, usize, usize) { + let mut me = mem::ManuallyDrop::new(self); + (me.as_mut_ptr(), me.len(), me.capacity()) + } + /// Creates a `Vec<T>` directly from the raw components of another vector. /// /// # Safety @@ -374,7 +413,11 @@ /// /// Violating these may cause problems like corrupting the allocator's /// internal data structures. For example it is **not** safe - /// to build a `Vec<u8>` from a pointer to a C `char` array and a `size_t`. + /// to build a `Vec<u8>` from a pointer to a C `char` array with length `size_t`. + /// It's also not safe to build one from a `Vec<u16>` and its length, because + /// the allocator cares about the alignment, and these two types have different + /// alignments. The buffer was allocated with alignment 2 (for `u16`), but after + /// turning it into a `Vec<u8>` it'll be deallocated with alignment 1. /// /// The ownership of `ptr` is effectively transferred to the /// `Vec<T>` which may then deallocate, reallocate or change the @@ -390,28 +433,27 @@ /// use std::ptr; /// use std::mem; /// - /// fn main() { - /// let mut v = vec![1, 2, 3]; + /// let v = vec![1, 2, 3]; /// - /// // Pull out the various important pieces of information about `v` - /// let p = v.as_mut_ptr(); - /// let len = v.len(); - /// let cap = v.capacity(); + // FIXME Update this when vec_into_raw_parts is stabilized + /// // Prevent running `v`'s destructor so we are in complete control + /// // of the allocation. + /// let mut v = mem::ManuallyDrop::new(v); /// - /// unsafe { - /// // Cast `v` into the void: no destructor run, so we are in - /// // complete control of the allocation to which `p` points. - /// mem::forget(v); + /// // Pull out the various important pieces of information about `v` + /// let p = v.as_mut_ptr(); + /// let len = v.len(); + /// let cap = v.capacity(); /// - /// // Overwrite memory with 4, 5, 6 - /// for i in 0..len as isize { - /// ptr::write(p.offset(i), 4 + i); - /// } - /// - /// // Put everything back together into a Vec - /// let rebuilt = Vec::from_raw_parts(p, len, cap); - /// assert_eq!(rebuilt, [4, 5, 6]); + /// unsafe { + /// // Overwrite memory with 4, 5, 6 + /// for i in 0..len as isize { + /// ptr::write(p.offset(i), 4 + i); /// } + /// + /// // Put everything back together into a Vec + /// let rebuilt = Vec::from_raw_parts(p, len, cap); + /// assert_eq!(rebuilt, [4, 5, 6]); /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -1392,12 +1434,10 @@ /// ``` /// #![feature(vec_leak)] /// - /// fn main() { - /// let x = vec![1, 2, 3]; - /// let static_ref: &'static mut [usize] = Vec::leak(x); - /// static_ref[0] += 1; - /// assert_eq!(static_ref, &[2, 2, 3]); - /// } + /// let x = vec![1, 2, 3]; + /// let static_ref: &'static mut [usize] = Vec::leak(x); + /// static_ref[0] += 1; + /// assert_eq!(static_ref, &[2, 2, 3]); /// ``` #[unstable(feature = "vec_leak", issue = "62195")] #[inline] @@ -1735,20 +1775,45 @@ impl_is_zero!(f32, |x: f32| x.to_bits() == 0); impl_is_zero!(f64, |x: f64| x.to_bits() == 0); -unsafe impl<T: ?Sized> IsZero for *const T { +unsafe impl<T> IsZero for *const T { #[inline] fn is_zero(&self) -> bool { (*self).is_null() } } -unsafe impl<T: ?Sized> IsZero for *mut T { +unsafe impl<T> IsZero for *mut T { #[inline] fn is_zero(&self) -> bool { (*self).is_null() } } +// `Option<&T>`, `Option<&mut T>` and `Option<Box<T>>` are guaranteed to represent `None` as null. +// For fat pointers, the bytes that would be the pointer metadata in the `Some` variant +// are padding in the `None` variant, so ignoring them and zero-initializing instead is ok. + +unsafe impl<T: ?Sized> IsZero for Option<&T> { + #[inline] + fn is_zero(&self) -> bool { + self.is_none() + } +} + +unsafe impl<T: ?Sized> IsZero for Option<&mut T> { + #[inline] + fn is_zero(&self) -> bool { + self.is_none() + } +} + +unsafe impl<T: ?Sized> IsZero for Option<Box<T>> { + #[inline] + fn is_zero(&self) -> bool { + self.is_none() + } +} + //////////////////////////////////////////////////////////////////////////////// // Common trait implementations for Vec
diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index 690d834..66d27a2 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs
@@ -500,7 +500,7 @@ // though it was supposed to give us `len` return slice::from_raw_parts_mut(mem, i); } - ptr::write(mem.offset(i as isize), value.unwrap()); + ptr::write(mem.add(i), value.unwrap()); i += 1; } }
diff --git a/src/libcore/any.rs b/src/libcore/any.rs index 0afbf4f..e2704e8 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs
@@ -2,14 +2,14 @@ //! of any `'static` type through runtime reflection. //! //! `Any` itself can be used to get a `TypeId`, and has more features when used -//! as a trait object. As `&Any` (a borrowed trait object), it has the `is` and -//! `downcast_ref` methods, to test if the contained value is of a given type, -//! and to get a reference to the inner value as a type. As `&mut Any`, there +//! as a trait object. As `&dyn Any` (a borrowed trait object), it has the `is` +//! and `downcast_ref` methods, to test if the contained value is of a given type, +//! and to get a reference to the inner value as a type. As `&mut dyn Any`, there //! is also the `downcast_mut` method, for getting a mutable reference to the -//! inner value. `Box<Any>` adds the `downcast` method, which attempts to +//! inner value. `Box<dyn Any>` adds the `downcast` method, which attempts to //! convert to a `Box<T>`. See the [`Box`] documentation for the full details. //! -//! Note that &Any is limited to testing whether a value is of a specified +//! Note that `&dyn Any` is limited to testing whether a value is of a specified //! concrete type, and cannot be used to test whether a type implements a trait. //! //! [`Box`]: ../../std/boxed/struct.Box.html @@ -87,10 +87,8 @@ /// TypeId::of::<String>() == s.type_id() /// } /// - /// fn main() { - /// assert_eq!(is_string(&0), false); - /// assert_eq!(is_string(&"cookie monster".to_string()), true); - /// } + /// assert_eq!(is_string(&0), false); + /// assert_eq!(is_string(&"cookie monster".to_string()), true); /// ``` #[stable(feature = "get_type_id", since = "1.34.0")] fn type_id(&self) -> TypeId; @@ -145,10 +143,8 @@ /// } /// } /// - /// fn main() { - /// is_string(&0); - /// is_string(&"cookie monster".to_string()); - /// } + /// is_string(&0); + /// is_string(&"cookie monster".to_string()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -179,10 +175,8 @@ /// } /// } /// - /// fn main() { - /// print_if_string(&0); - /// print_if_string(&"cookie monster".to_string()); - /// } + /// print_if_string(&0); + /// print_if_string(&"cookie monster".to_string()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -210,16 +204,14 @@ /// } /// } /// - /// fn main() { - /// let mut x = 10u32; - /// let mut s = "starlord".to_string(); + /// let mut x = 10u32; + /// let mut s = "starlord".to_string(); /// - /// modify_if_u32(&mut x); - /// modify_if_u32(&mut s); + /// modify_if_u32(&mut x); + /// modify_if_u32(&mut s); /// - /// assert_eq!(x, 42); - /// assert_eq!(&s, "starlord"); - /// } + /// assert_eq!(x, 42); + /// assert_eq!(&s, "starlord"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -250,10 +242,8 @@ /// } /// } /// - /// fn main() { - /// is_string(&0); - /// is_string(&"cookie monster".to_string()); - /// } + /// is_string(&0); + /// is_string(&"cookie monster".to_string()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -276,10 +266,8 @@ /// } /// } /// - /// fn main() { - /// print_if_string(&0); - /// print_if_string(&"cookie monster".to_string()); - /// } + /// print_if_string(&0); + /// print_if_string(&"cookie monster".to_string()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -300,16 +288,14 @@ /// } /// } /// - /// fn main() { - /// let mut x = 10u32; - /// let mut s = "starlord".to_string(); + /// let mut x = 10u32; + /// let mut s = "starlord".to_string(); /// - /// modify_if_u32(&mut x); - /// modify_if_u32(&mut s); + /// modify_if_u32(&mut x); + /// modify_if_u32(&mut s); /// - /// assert_eq!(x, 42); - /// assert_eq!(&s, "starlord"); - /// } + /// assert_eq!(x, 42); + /// assert_eq!(&s, "starlord"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -334,10 +320,8 @@ /// } /// } /// - /// fn main() { - /// is_string(&0); - /// is_string(&"cookie monster".to_string()); - /// } + /// is_string(&0); + /// is_string(&"cookie monster".to_string()); /// ``` #[stable(feature = "any_send_sync_methods", since = "1.28.0")] #[inline] @@ -360,10 +344,8 @@ /// } /// } /// - /// fn main() { - /// print_if_string(&0); - /// print_if_string(&"cookie monster".to_string()); - /// } + /// print_if_string(&0); + /// print_if_string(&"cookie monster".to_string()); /// ``` #[stable(feature = "any_send_sync_methods", since = "1.28.0")] #[inline] @@ -384,16 +366,14 @@ /// } /// } /// - /// fn main() { - /// let mut x = 10u32; - /// let mut s = "starlord".to_string(); + /// let mut x = 10u32; + /// let mut s = "starlord".to_string(); /// - /// modify_if_u32(&mut x); - /// modify_if_u32(&mut s); + /// modify_if_u32(&mut x); + /// modify_if_u32(&mut s); /// - /// assert_eq!(x, 42); - /// assert_eq!(&s, "starlord"); - /// } + /// assert_eq!(x, 42); + /// assert_eq!(&s, "starlord"); /// ``` #[stable(feature = "any_send_sync_methods", since = "1.28.0")] #[inline] @@ -437,10 +417,8 @@ /// TypeId::of::<String>() == TypeId::of::<T>() /// } /// - /// fn main() { - /// assert_eq!(is_string(&0), false); - /// assert_eq!(is_string(&"cookie monster".to_string()), true); - /// } + /// assert_eq!(is_string(&0), false); + /// assert_eq!(is_string(&"cookie monster".to_string()), true); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature="const_type_id")] @@ -467,6 +445,15 @@ /// /// The current implementation uses the same infrastructure as compiler /// diagnostics and debuginfo, but this is not guaranteed. +/// +/// # Example +/// +/// ```rust +/// assert_eq!( +/// std::any::type_name::<Option<String>>(), +/// "core::option::Option<alloc::string::String>", +/// ); +/// ``` #[stable(feature = "type_name", since = "1.38.0")] #[rustc_const_unstable(feature = "const_type_name")] pub const fn type_name<T: ?Sized>() -> &'static str {
diff --git a/src/libcore/array.rs b/src/libcore/array.rs deleted file mode 100644 index b561401..0000000 --- a/src/libcore/array.rs +++ /dev/null
@@ -1,425 +0,0 @@ -//! Implementations of things like `Eq` for fixed-length arrays -//! up to a certain length. Eventually we should able to generalize -//! to all lengths. -//! -//! *[See also the array primitive type](../../std/primitive.array.html).* - -#![stable(feature = "core_array", since = "1.36.0")] - -use crate::borrow::{Borrow, BorrowMut}; -use crate::cmp::Ordering; -use crate::convert::{Infallible, TryFrom}; -use crate::fmt; -use crate::hash::{Hash, self}; -use crate::marker::Unsize; -use crate::slice::{Iter, IterMut}; - -/// Utility trait implemented only on arrays of fixed size -/// -/// This trait can be used to implement other traits on fixed-size arrays -/// without causing much metadata bloat. -/// -/// The trait is marked unsafe in order to restrict implementors to fixed-size -/// arrays. User of this trait can assume that implementors have the exact -/// layout in memory of a fixed size array (for example, for unsafe -/// initialization). -/// -/// Note that the traits [`AsRef`] and [`AsMut`] provide similar methods for types that -/// may not be fixed-size arrays. Implementors should prefer those traits -/// instead. -/// -/// [`AsRef`]: ../convert/trait.AsRef.html -/// [`AsMut`]: ../convert/trait.AsMut.html -#[unstable(feature = "fixed_size_array", issue = "27778")] -pub unsafe trait FixedSizeArray<T> { - /// Converts the array to immutable slice - #[unstable(feature = "fixed_size_array", issue = "27778")] - fn as_slice(&self) -> &[T]; - /// Converts the array to mutable slice - #[unstable(feature = "fixed_size_array", issue = "27778")] - fn as_mut_slice(&mut self) -> &mut [T]; -} - -#[unstable(feature = "fixed_size_array", issue = "27778")] -unsafe impl<T, A: Unsize<[T]>> FixedSizeArray<T> for A { - #[inline] - fn as_slice(&self) -> &[T] { - self - } - #[inline] - fn as_mut_slice(&mut self) -> &mut [T] { - self - } -} - -/// The error type returned when a conversion from a slice to an array fails. -#[stable(feature = "try_from", since = "1.34.0")] -#[derive(Debug, Copy, Clone)] -pub struct TryFromSliceError(()); - -#[stable(feature = "core_array", since = "1.36.0")] -impl fmt::Display for TryFromSliceError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.__description(), f) - } -} - -impl TryFromSliceError { - #[unstable(feature = "array_error_internals", - reason = "available through Error trait and this method should not \ - be exposed publicly", - issue = "0")] - #[inline] - #[doc(hidden)] - pub fn __description(&self) -> &str { - "could not convert slice to array" - } -} - -#[stable(feature = "try_from_slice_error", since = "1.36.0")] -impl From<Infallible> for TryFromSliceError { - fn from(x: Infallible) -> TryFromSliceError { - match x {} - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T, const N: usize> AsRef<[T]> for [T; N] -where - [T; N]: LengthAtMost32, -{ - #[inline] - fn as_ref(&self) -> &[T] { - &self[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T, const N: usize> AsMut<[T]> for [T; N] -where - [T; N]: LengthAtMost32, -{ - #[inline] - fn as_mut(&mut self) -> &mut [T] { - &mut self[..] - } -} - -#[stable(feature = "array_borrow", since = "1.4.0")] -impl<T, const N: usize> Borrow<[T]> for [T; N] -where - [T; N]: LengthAtMost32, -{ - fn borrow(&self) -> &[T] { - self - } -} - -#[stable(feature = "array_borrow", since = "1.4.0")] -impl<T, const N: usize> BorrowMut<[T]> for [T; N] -where - [T; N]: LengthAtMost32, -{ - fn borrow_mut(&mut self) -> &mut [T] { - self - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl<T, const N: usize> TryFrom<&[T]> for [T; N] -where - T: Copy, - [T; N]: LengthAtMost32, -{ - type Error = TryFromSliceError; - - fn try_from(slice: &[T]) -> Result<[T; N], TryFromSliceError> { - <&Self>::try_from(slice).map(|r| *r) - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N] -where - [T; N]: LengthAtMost32, -{ - type Error = TryFromSliceError; - - fn try_from(slice: &[T]) -> Result<&[T; N], TryFromSliceError> { - if slice.len() == N { - let ptr = slice.as_ptr() as *const [T; N]; - unsafe { Ok(&*ptr) } - } else { - Err(TryFromSliceError(())) - } - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N] -where - [T; N]: LengthAtMost32, -{ - type Error = TryFromSliceError; - - fn try_from(slice: &mut [T]) -> Result<&mut [T; N], TryFromSliceError> { - if slice.len() == N { - let ptr = slice.as_mut_ptr() as *mut [T; N]; - unsafe { Ok(&mut *ptr) } - } else { - Err(TryFromSliceError(())) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: Hash, const N: usize> Hash for [T; N] -where - [T; N]: LengthAtMost32, -{ - fn hash<H: hash::Hasher>(&self, state: &mut H) { - Hash::hash(&self[..], state) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: fmt::Debug, const N: usize> fmt::Debug for [T; N] -where - [T; N]: LengthAtMost32, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&&self[..], f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, const N: usize> IntoIterator for &'a [T; N] -where - [T; N]: LengthAtMost32, -{ - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, const N: usize> IntoIterator for &'a mut [T; N] -where - [T; N]: LengthAtMost32, -{ - type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - - fn into_iter(self) -> IterMut<'a, T> { - self.iter_mut() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<A, B, const N: usize> PartialEq<[B; N]> for [A; N] -where - A: PartialEq<B>, - [A; N]: LengthAtMost32, - [B; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &[B; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[B; N]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<A, B, const N: usize> PartialEq<[B]> for [A; N] -where - A: PartialEq<B>, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &[B]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[B]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<A, B, const N: usize> PartialEq<[A; N]> for [B] -where - B: PartialEq<A>, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &[A; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[A; N]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'b, A, B, const N: usize> PartialEq<&'b [B]> for [A; N] -where - A: PartialEq<B>, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &&'b [B]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &&'b [B]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'b, A, B, const N: usize> PartialEq<[A; N]> for &'b [B] -where - B: PartialEq<A>, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &[A; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[A; N]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'b, A, B, const N: usize> PartialEq<&'b mut [B]> for [A; N] -where - A: PartialEq<B>, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &&'b mut [B]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &&'b mut [B]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'b, A, B, const N: usize> PartialEq<[A; N]> for &'b mut [B] -where - B: PartialEq<A>, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &[A; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[A; N]) -> bool { - self[..] != other[..] - } -} - -// NOTE: some less important impls are omitted to reduce code bloat -// __impl_slice_eq2! { [A; $N], &'b [B; $N] } -// __impl_slice_eq2! { [A; $N], &'b mut [B; $N] } - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: Eq, const N: usize> Eq for [T; N] where [T; N]: LengthAtMost32 {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: PartialOrd, const N: usize> PartialOrd for [T; N] -where - [T; N]: LengthAtMost32, -{ - #[inline] - fn partial_cmp(&self, other: &[T; N]) -> Option<Ordering> { - PartialOrd::partial_cmp(&&self[..], &&other[..]) - } - #[inline] - fn lt(&self, other: &[T; N]) -> bool { - PartialOrd::lt(&&self[..], &&other[..]) - } - #[inline] - fn le(&self, other: &[T; N]) -> bool { - PartialOrd::le(&&self[..], &&other[..]) - } - #[inline] - fn ge(&self, other: &[T; N]) -> bool { - PartialOrd::ge(&&self[..], &&other[..]) - } - #[inline] - fn gt(&self, other: &[T; N]) -> bool { - PartialOrd::gt(&&self[..], &&other[..]) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: Ord, const N: usize> Ord for [T; N] -where - [T; N]: LengthAtMost32, -{ - #[inline] - fn cmp(&self, other: &[T; N]) -> Ordering { - Ord::cmp(&&self[..], &&other[..]) - } -} - -/// Implemented for lengths where trait impls are allowed on arrays in core/std -#[rustc_on_unimplemented( - message="arrays only have std trait implementations for lengths 0..=32", -)] -#[unstable(feature = "const_generic_impls_guard", issue = "0", - reason = "will never be stable, just a temporary step until const generics are stable")] -pub trait LengthAtMost32 {} - -macro_rules! array_impls { - ($($N:literal)+) => { - $( - #[unstable(feature = "const_generic_impls_guard", issue = "0")] - impl<T> LengthAtMost32 for [T; $N] {} - )+ - } -} - -array_impls! { - 0 1 2 3 4 5 6 7 8 9 - 10 11 12 13 14 15 16 17 18 19 - 20 21 22 23 24 25 26 27 28 29 - 30 31 32 -} - -// The Default impls cannot be generated using the array_impls! macro because -// they require array literals. - -macro_rules! array_impl_default { - {$n:expr, $t:ident $($ts:ident)*} => { - #[stable(since = "1.4.0", feature = "array_default")] - impl<T> Default for [T; $n] where T: Default { - fn default() -> [T; $n] { - [$t::default(), $($ts::default()),*] - } - } - array_impl_default!{($n - 1), $($ts)*} - }; - {$n:expr,} => { - #[stable(since = "1.4.0", feature = "array_default")] - impl<T> Default for [T; $n] { - fn default() -> [T; $n] { [] } - } - }; -} - -array_impl_default!{32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T}
diff --git a/src/libcore/array/iter.rs b/src/libcore/array/iter.rs new file mode 100644 index 0000000..1180323 --- /dev/null +++ b/src/libcore/array/iter.rs
@@ -0,0 +1,266 @@ +//! Defines the `IntoIter` owned iterator for arrays. + +use crate::{ + fmt, + iter::{ExactSizeIterator, FusedIterator, TrustedLen}, + mem::{self, MaybeUninit}, + ops::Range, + ptr, +}; +use super::LengthAtMost32; + + +/// A by-value [array] iterator. +/// +/// [array]: ../../std/primitive.array.html +#[unstable(feature = "array_value_iter", issue = "65798")] +pub struct IntoIter<T, const N: usize> +where + [T; N]: LengthAtMost32, +{ + /// This is the array we are iterating over. + /// + /// Elements with index `i` where `alive.start <= i < alive.end` have not + /// been yielded yet and are valid array entries. Elements with indices `i + /// < alive.start` or `i >= alive.end` have been yielded already and must + /// not be accessed anymore! Those dead elements might even be in a + /// completely uninitialized state! + /// + /// So the invariants are: + /// - `data[alive]` is alive (i.e. contains valid elements) + /// - `data[..alive.start]` and `data[alive.end..]` are dead (i.e. the + /// elements were already read and must not be touched anymore!) + data: [MaybeUninit<T>; N], + + /// The elements in `data` that have not been yielded yet. + /// + /// Invariants: + /// - `alive.start <= alive.end` + /// - `alive.end <= N` + alive: Range<usize>, +} + +impl<T, const N: usize> IntoIter<T, {N}> +where + [T; N]: LengthAtMost32, +{ + /// Creates a new iterator over the given `array`. + /// + /// *Note*: this method might never get stabilized and/or removed in the + /// future as there will likely be another, preferred way of obtaining this + /// iterator (either via `IntoIterator` for arrays or via another way). + #[unstable(feature = "array_value_iter", issue = "65798")] + pub fn new(array: [T; N]) -> Self { + // The transmute here is actually safe. The docs of `MaybeUninit` + // promise: + // + // > `MaybeUninit<T>` is guaranteed to have the same size and alignment + // > as `T`. + // + // The docs even show a transmute from an array of `MaybeUninit<T>` to + // an array of `T`. + // + // With that, this initialization satisfies the invariants. + + // FIXME(LukasKalbertodt): actually use `mem::transmute` here, once it + // works with const generics: + // `mem::transmute::<[T; {N}], [MaybeUninit<T>; {N}]>(array)` + // + // Until then, we do it manually here. We first create a bitwise copy + // but cast the pointer so that it is treated as a different type. Then + // we forget `array` so that it is not dropped. + let data = unsafe { + let data = ptr::read(&array as *const [T; N] as *const [MaybeUninit<T>; N]); + mem::forget(array); + data + }; + + Self { + data, + alive: 0..N, + } + } + + /// Returns an immutable slice of all elements that have not been yielded + /// yet. + fn as_slice(&self) -> &[T] { + // This transmute is safe. As mentioned in `new`, `MaybeUninit` retains + // the size and alignment of `T`. Furthermore, we know that all + // elements within `alive` are properly initialized. + let slice = &self.data[self.alive.clone()]; + unsafe { + mem::transmute::<&[MaybeUninit<T>], &[T]>(slice) + } + } +} + + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl<T, const N: usize> Iterator for IntoIter<T, {N}> +where + [T; N]: LengthAtMost32, +{ + type Item = T; + fn next(&mut self) -> Option<Self::Item> { + if self.alive.start == self.alive.end { + return None; + } + + // Bump start index. + // + // From the check above we know that `alive.start != alive.end`. + // Combine this with the invariant `alive.start <= alive.end`, we know + // that `alive.start < alive.end`. Increasing `alive.start` by 1 + // maintains the invariant regarding `alive`. However, due to this + // change, for a short time, the alive zone is not `data[alive]` + // anymore, but `data[idx..alive.end]`. + let idx = self.alive.start; + self.alive.start += 1; + + // Read the element from the array. This is safe: `idx` is an index + // into the "alive" region of the array. Reading this element means + // that `data[idx]` is regarded as dead now (i.e. do not touch). As + // `idx` was the start of the alive-zone, the alive zone is now + // `data[alive]` again, restoring all invariants. + let out = unsafe { self.data.get_unchecked(idx).read() }; + + Some(out) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let len = self.len(); + (len, Some(len)) + } + + fn count(self) -> usize { + self.len() + } + + fn last(mut self) -> Option<Self::Item> { + self.next_back() + } +} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl<T, const N: usize> DoubleEndedIterator for IntoIter<T, {N}> +where + [T; N]: LengthAtMost32, +{ + fn next_back(&mut self) -> Option<Self::Item> { + if self.alive.start == self.alive.end { + return None; + } + + // Decrease end index. + // + // From the check above we know that `alive.start != alive.end`. + // Combine this with the invariant `alive.start <= alive.end`, we know + // that `alive.start < alive.end`. As `alive.start` cannot be negative, + // `alive.end` is at least 1, meaning that we can safely decrement it + // by one. This also maintains the invariant `alive.start <= + // alive.end`. However, due to this change, for a short time, the alive + // zone is not `data[alive]` anymore, but `data[alive.start..alive.end + // + 1]`. + self.alive.end -= 1; + + // Read the element from the array. This is safe: `alive.end` is an + // index into the "alive" region of the array. Compare the previous + // comment that states that the alive region is + // `data[alive.start..alive.end + 1]`. Reading this element means that + // `data[alive.end]` is regarded as dead now (i.e. do not touch). As + // `alive.end` was the end of the alive-zone, the alive zone is now + // `data[alive]` again, restoring all invariants. + let out = unsafe { self.data.get_unchecked(self.alive.end).read() }; + + Some(out) + } +} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl<T, const N: usize> Drop for IntoIter<T, {N}> +where + [T; N]: LengthAtMost32, +{ + fn drop(&mut self) { + // We simply drop each element via `for_each`. This should not incur + // any significant runtime overhead and avoids adding another `unsafe` + // block. + self.by_ref().for_each(drop); + } +} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl<T, const N: usize> ExactSizeIterator for IntoIter<T, {N}> +where + [T; N]: LengthAtMost32, +{ + fn len(&self) -> usize { + // Will never underflow due to the invariant `alive.start <= + // alive.end`. + self.alive.end - self.alive.start + } + fn is_empty(&self) -> bool { + self.alive.is_empty() + } +} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl<T, const N: usize> FusedIterator for IntoIter<T, {N}> +where + [T; N]: LengthAtMost32, +{} + +// The iterator indeed reports the correct length. The number of "alive" +// elements (that will still be yielded) is the length of the range `alive`. +// This range is decremented in length in either `next` or `next_back`. It is +// always decremented by 1 in those methods, but only if `Some(_)` is returned. +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +unsafe impl<T, const N: usize> TrustedLen for IntoIter<T, {N}> +where + [T; N]: LengthAtMost32, +{} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl<T: Clone, const N: usize> Clone for IntoIter<T, {N}> +where + [T; N]: LengthAtMost32, +{ + fn clone(&self) -> Self { + unsafe { + // This creates a new uninitialized array. Note that the `assume_init` + // refers to the array, not the individual elements. And it is Ok if + // the array is in an uninitialized state as all elements may be + // uninitialized (all bit patterns are valid). Compare the + // `MaybeUninit` docs for more information. + let mut new_data: [MaybeUninit<T>; N] = MaybeUninit::uninit().assume_init(); + + // Clone all alive elements. + for idx in self.alive.clone() { + // The element at `idx` in the old array is alive, so we can + // safely call `get_ref()`. We then clone it, and write the + // clone into the new array. + let clone = self.data.get_unchecked(idx).get_ref().clone(); + new_data.get_unchecked_mut(idx).write(clone); + } + + Self { + data: new_data, + alive: self.alive.clone(), + } + } + } +} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl<T: fmt::Debug, const N: usize> fmt::Debug for IntoIter<T, {N}> +where + [T; N]: LengthAtMost32, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Only print the elements that were not yielded yet: we cannot + // access the yielded elements anymore. + f.debug_tuple("IntoIter") + .field(&self.as_slice()) + .finish() + } +}
diff --git a/src/libcore/array/mod.rs b/src/libcore/array/mod.rs new file mode 100644 index 0000000..e1ec8b7 --- /dev/null +++ b/src/libcore/array/mod.rs
@@ -0,0 +1,432 @@ +//! Implementations of things like `Eq` for fixed-length arrays +//! up to a certain length. Eventually we should able to generalize +//! to all lengths. +//! +//! *[See also the array primitive type](../../std/primitive.array.html).* + +#![stable(feature = "core_array", since = "1.36.0")] + +use crate::borrow::{Borrow, BorrowMut}; +use crate::cmp::Ordering; +use crate::convert::{Infallible, TryFrom}; +use crate::fmt; +use crate::hash::{Hash, self}; +use crate::marker::Unsize; +use crate::slice::{Iter, IterMut}; + +#[cfg(not(bootstrap))] +mod iter; + +#[cfg(not(bootstrap))] +#[unstable(feature = "array_value_iter", issue = "65798")] +pub use iter::IntoIter; + +/// Utility trait implemented only on arrays of fixed size +/// +/// This trait can be used to implement other traits on fixed-size arrays +/// without causing much metadata bloat. +/// +/// The trait is marked unsafe in order to restrict implementors to fixed-size +/// arrays. User of this trait can assume that implementors have the exact +/// layout in memory of a fixed size array (for example, for unsafe +/// initialization). +/// +/// Note that the traits [`AsRef`] and [`AsMut`] provide similar methods for types that +/// may not be fixed-size arrays. Implementors should prefer those traits +/// instead. +/// +/// [`AsRef`]: ../convert/trait.AsRef.html +/// [`AsMut`]: ../convert/trait.AsMut.html +#[unstable(feature = "fixed_size_array", issue = "27778")] +pub unsafe trait FixedSizeArray<T> { + /// Converts the array to immutable slice + #[unstable(feature = "fixed_size_array", issue = "27778")] + fn as_slice(&self) -> &[T]; + /// Converts the array to mutable slice + #[unstable(feature = "fixed_size_array", issue = "27778")] + fn as_mut_slice(&mut self) -> &mut [T]; +} + +#[unstable(feature = "fixed_size_array", issue = "27778")] +unsafe impl<T, A: Unsize<[T]>> FixedSizeArray<T> for A { + #[inline] + fn as_slice(&self) -> &[T] { + self + } + #[inline] + fn as_mut_slice(&mut self) -> &mut [T] { + self + } +} + +/// The error type returned when a conversion from a slice to an array fails. +#[stable(feature = "try_from", since = "1.34.0")] +#[derive(Debug, Copy, Clone)] +pub struct TryFromSliceError(()); + +#[stable(feature = "core_array", since = "1.36.0")] +impl fmt::Display for TryFromSliceError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.__description(), f) + } +} + +impl TryFromSliceError { + #[unstable(feature = "array_error_internals", + reason = "available through Error trait and this method should not \ + be exposed publicly", + issue = "0")] + #[inline] + #[doc(hidden)] + pub fn __description(&self) -> &str { + "could not convert slice to array" + } +} + +#[stable(feature = "try_from_slice_error", since = "1.36.0")] +impl From<Infallible> for TryFromSliceError { + fn from(x: Infallible) -> TryFromSliceError { + match x {} + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T, const N: usize> AsRef<[T]> for [T; N] +where + [T; N]: LengthAtMost32, +{ + #[inline] + fn as_ref(&self) -> &[T] { + &self[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T, const N: usize> AsMut<[T]> for [T; N] +where + [T; N]: LengthAtMost32, +{ + #[inline] + fn as_mut(&mut self) -> &mut [T] { + &mut self[..] + } +} + +#[stable(feature = "array_borrow", since = "1.4.0")] +impl<T, const N: usize> Borrow<[T]> for [T; N] +where + [T; N]: LengthAtMost32, +{ + fn borrow(&self) -> &[T] { + self + } +} + +#[stable(feature = "array_borrow", since = "1.4.0")] +impl<T, const N: usize> BorrowMut<[T]> for [T; N] +where + [T; N]: LengthAtMost32, +{ + fn borrow_mut(&mut self) -> &mut [T] { + self + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl<T, const N: usize> TryFrom<&[T]> for [T; N] +where + T: Copy, + [T; N]: LengthAtMost32, +{ + type Error = TryFromSliceError; + + fn try_from(slice: &[T]) -> Result<[T; N], TryFromSliceError> { + <&Self>::try_from(slice).map(|r| *r) + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N] +where + [T; N]: LengthAtMost32, +{ + type Error = TryFromSliceError; + + fn try_from(slice: &[T]) -> Result<&[T; N], TryFromSliceError> { + if slice.len() == N { + let ptr = slice.as_ptr() as *const [T; N]; + unsafe { Ok(&*ptr) } + } else { + Err(TryFromSliceError(())) + } + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N] +where + [T; N]: LengthAtMost32, +{ + type Error = TryFromSliceError; + + fn try_from(slice: &mut [T]) -> Result<&mut [T; N], TryFromSliceError> { + if slice.len() == N { + let ptr = slice.as_mut_ptr() as *mut [T; N]; + unsafe { Ok(&mut *ptr) } + } else { + Err(TryFromSliceError(())) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: Hash, const N: usize> Hash for [T; N] +where + [T; N]: LengthAtMost32, +{ + fn hash<H: hash::Hasher>(&self, state: &mut H) { + Hash::hash(&self[..], state) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: fmt::Debug, const N: usize> fmt::Debug for [T; N] +where + [T; N]: LengthAtMost32, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&&self[..], f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, const N: usize> IntoIterator for &'a [T; N] +where + [T; N]: LengthAtMost32, +{ + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, const N: usize> IntoIterator for &'a mut [T; N] +where + [T; N]: LengthAtMost32, +{ + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<A, B, const N: usize> PartialEq<[B; N]> for [A; N] +where + A: PartialEq<B>, + [A; N]: LengthAtMost32, + [B; N]: LengthAtMost32, +{ + #[inline] + fn eq(&self, other: &[B; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[B; N]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<A, B, const N: usize> PartialEq<[B]> for [A; N] +where + A: PartialEq<B>, + [A; N]: LengthAtMost32, +{ + #[inline] + fn eq(&self, other: &[B]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[B]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<A, B, const N: usize> PartialEq<[A; N]> for [B] +where + B: PartialEq<A>, + [A; N]: LengthAtMost32, +{ + #[inline] + fn eq(&self, other: &[A; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[A; N]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'b, A, B, const N: usize> PartialEq<&'b [B]> for [A; N] +where + A: PartialEq<B>, + [A; N]: LengthAtMost32, +{ + #[inline] + fn eq(&self, other: &&'b [B]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &&'b [B]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'b, A, B, const N: usize> PartialEq<[A; N]> for &'b [B] +where + B: PartialEq<A>, + [A; N]: LengthAtMost32, +{ + #[inline] + fn eq(&self, other: &[A; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[A; N]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'b, A, B, const N: usize> PartialEq<&'b mut [B]> for [A; N] +where + A: PartialEq<B>, + [A; N]: LengthAtMost32, +{ + #[inline] + fn eq(&self, other: &&'b mut [B]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &&'b mut [B]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'b, A, B, const N: usize> PartialEq<[A; N]> for &'b mut [B] +where + B: PartialEq<A>, + [A; N]: LengthAtMost32, +{ + #[inline] + fn eq(&self, other: &[A; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[A; N]) -> bool { + self[..] != other[..] + } +} + +// NOTE: some less important impls are omitted to reduce code bloat +// __impl_slice_eq2! { [A; $N], &'b [B; $N] } +// __impl_slice_eq2! { [A; $N], &'b mut [B; $N] } + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: Eq, const N: usize> Eq for [T; N] where [T; N]: LengthAtMost32 {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: PartialOrd, const N: usize> PartialOrd for [T; N] +where + [T; N]: LengthAtMost32, +{ + #[inline] + fn partial_cmp(&self, other: &[T; N]) -> Option<Ordering> { + PartialOrd::partial_cmp(&&self[..], &&other[..]) + } + #[inline] + fn lt(&self, other: &[T; N]) -> bool { + PartialOrd::lt(&&self[..], &&other[..]) + } + #[inline] + fn le(&self, other: &[T; N]) -> bool { + PartialOrd::le(&&self[..], &&other[..]) + } + #[inline] + fn ge(&self, other: &[T; N]) -> bool { + PartialOrd::ge(&&self[..], &&other[..]) + } + #[inline] + fn gt(&self, other: &[T; N]) -> bool { + PartialOrd::gt(&&self[..], &&other[..]) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: Ord, const N: usize> Ord for [T; N] +where + [T; N]: LengthAtMost32, +{ + #[inline] + fn cmp(&self, other: &[T; N]) -> Ordering { + Ord::cmp(&&self[..], &&other[..]) + } +} + +/// Implemented for lengths where trait impls are allowed on arrays in core/std +#[rustc_on_unimplemented( + message="arrays only have std trait implementations for lengths 0..=32", +)] +#[unstable(feature = "const_generic_impls_guard", issue = "0", + reason = "will never be stable, just a temporary step until const generics are stable")] +pub trait LengthAtMost32 {} + +macro_rules! array_impls { + ($($N:literal)+) => { + $( + #[unstable(feature = "const_generic_impls_guard", issue = "0")] + impl<T> LengthAtMost32 for [T; $N] {} + )+ + } +} + +array_impls! { + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 +} + +// The Default impls cannot be generated using the array_impls! macro because +// they require array literals. + +macro_rules! array_impl_default { + {$n:expr, $t:ident $($ts:ident)*} => { + #[stable(since = "1.4.0", feature = "array_default")] + impl<T> Default for [T; $n] where T: Default { + fn default() -> [T; $n] { + [$t::default(), $($ts::default()),*] + } + } + array_impl_default!{($n - 1), $($ts)*} + }; + {$n:expr,} => { + #[stable(since = "1.4.0", feature = "array_default")] + impl<T> Default for [T; $n] { + fn default() -> [T; $n] { [] } + } + }; +} + +array_impl_default!{32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T}
diff --git a/src/libcore/bool.rs b/src/libcore/bool.rs index f751ccb..617bdd2 100644 --- a/src/libcore/bool.rs +++ b/src/libcore/bool.rs
@@ -1,6 +1,5 @@ //! impl bool {} -#[cfg(not(bootstrap))] #[lang = "bool"] impl bool { /// Returns `Some(t)` if the `bool` is `true`, or `None` otherwise.
diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 8579dbf..fda103a 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs
@@ -229,52 +229,6 @@ value: UnsafeCell<T>, } -impl<T:Copy> Cell<T> { - /// Returns a copy of the contained value. - /// - /// # Examples - /// - /// ``` - /// use std::cell::Cell; - /// - /// let c = Cell::new(5); - /// - /// let five = c.get(); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get(&self) -> T { - unsafe{ *self.value.get() } - } - - /// Updates the contained value using a function and returns the new value. - /// - /// # Examples - /// - /// ``` - /// #![feature(cell_update)] - /// - /// use std::cell::Cell; - /// - /// let c = Cell::new(5); - /// let new = c.update(|x| x + 1); - /// - /// assert_eq!(new, 6); - /// assert_eq!(c.get(), 6); - /// ``` - #[inline] - #[unstable(feature = "cell_update", issue = "50186")] - pub fn update<F>(&self, f: F) -> T - where - F: FnOnce(T) -> T, - { - let old = self.get(); - let new = f(old); - self.set(new); - new - } -} - #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: ?Sized> Send for Cell<T> where T: Send {} @@ -448,6 +402,52 @@ } } +impl<T:Copy> Cell<T> { + /// Returns a copy of the contained value. + /// + /// # Examples + /// + /// ``` + /// use std::cell::Cell; + /// + /// let c = Cell::new(5); + /// + /// let five = c.get(); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get(&self) -> T { + unsafe{ *self.value.get() } + } + + /// Updates the contained value using a function and returns the new value. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_update)] + /// + /// use std::cell::Cell; + /// + /// let c = Cell::new(5); + /// let new = c.update(|x| x + 1); + /// + /// assert_eq!(new, 6); + /// assert_eq!(c.get(), 6); + /// ``` + #[inline] + #[unstable(feature = "cell_update", issue = "50186")] + pub fn update<F>(&self, f: F) -> T + where + F: FnOnce(T) -> T, + { + let old = self.get(); + let new = f(old); + self.set(new); + new + } +} + impl<T: ?Sized> Cell<T> { /// Returns a raw pointer to the underlying data in this cell. ///
diff --git a/src/libcore/char/convert.rs b/src/libcore/char/convert.rs index 0a870c6..c456e14 100644 --- a/src/libcore/char/convert.rs +++ b/src/libcore/char/convert.rs
@@ -111,11 +111,9 @@ /// ``` /// use std::mem; /// - /// fn main() { - /// let c = 'c'; - /// let u = u32::from(c); - /// assert!(4 == mem::size_of_val(&u)) - /// } + /// let c = 'c'; + /// let u = u32::from(c); + /// assert!(4 == mem::size_of_val(&u)) /// ``` #[inline] fn from(c: char) -> Self { @@ -150,11 +148,9 @@ /// ``` /// use std::mem; /// - /// fn main() { - /// let u = 32 as u8; - /// let c = char::from(u); - /// assert!(4 == mem::size_of_val(&c)) - /// } + /// let u = 32 as u8; + /// let c = char::from(u); + /// assert!(4 == mem::size_of_val(&c)) /// ``` #[inline] fn from(i: u8) -> Self {
diff --git a/src/libcore/char/decode.rs b/src/libcore/char/decode.rs index 2305924..b71c9c2 100644 --- a/src/libcore/char/decode.rs +++ b/src/libcore/char/decode.rs
@@ -31,21 +31,23 @@ /// ``` /// use std::char::decode_utf16; /// -/// fn main() { -/// // 𝄞mus<invalid>ic<invalid> -/// let v = [0xD834, 0xDD1E, 0x006d, 0x0075, -/// 0x0073, 0xDD1E, 0x0069, 0x0063, -/// 0xD834]; +/// // 𝄞mus<invalid>ic<invalid> +/// let v = [ +/// 0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, +/// ]; /// -/// assert_eq!(decode_utf16(v.iter().cloned()) -/// .map(|r| r.map_err(|e| e.unpaired_surrogate())) -/// .collect::<Vec<_>>(), -/// vec![Ok('𝄞'), -/// Ok('m'), Ok('u'), Ok('s'), -/// Err(0xDD1E), -/// Ok('i'), Ok('c'), -/// Err(0xD834)]); -/// } +/// assert_eq!( +/// decode_utf16(v.iter().cloned()) +/// .map(|r| r.map_err(|e| e.unpaired_surrogate())) +/// .collect::<Vec<_>>(), +/// vec![ +/// Ok('𝄞'), +/// Ok('m'), Ok('u'), Ok('s'), +/// Err(0xDD1E), +/// Ok('i'), Ok('c'), +/// Err(0xD834) +/// ] +/// ); /// ``` /// /// A lossy decoder can be obtained by replacing `Err` results with the replacement character: @@ -53,17 +55,17 @@ /// ``` /// use std::char::{decode_utf16, REPLACEMENT_CHARACTER}; /// -/// fn main() { -/// // 𝄞mus<invalid>ic<invalid> -/// let v = [0xD834, 0xDD1E, 0x006d, 0x0075, -/// 0x0073, 0xDD1E, 0x0069, 0x0063, -/// 0xD834]; +/// // 𝄞mus<invalid>ic<invalid> +/// let v = [ +/// 0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, +/// ]; /// -/// assert_eq!(decode_utf16(v.iter().cloned()) -/// .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) -/// .collect::<String>(), -/// "𝄞mus�ic�"); -/// } +/// assert_eq!( +/// decode_utf16(v.iter().cloned()) +/// .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) +/// .collect::<String>(), +/// "𝄞mus�ic�" +/// ); /// ``` #[stable(feature = "decode_utf16", since = "1.9.0")] #[inline]
diff --git a/src/libcore/char/methods.rs b/src/libcore/char/methods.rs index a69eb0f..971d89e 100644 --- a/src/libcore/char/methods.rs +++ b/src/libcore/char/methods.rs
@@ -116,9 +116,9 @@ // the code is split up here to improve execution speed for cases where // the `radix` is constant and 10 or smaller - let val = if radix <= 10 { + let val = if radix <= 10 { match self { - '0' ..= '9' => self as u32 - '0' as u32, + '0'..='9' => self as u32 - '0' as u32, _ => return None, } } else { @@ -130,8 +130,11 @@ } }; - if val < radix { Some(val) } - else { None } + if val < radix { + Some(val) + } else { + None + } } /// Returns an iterator that yields the hexadecimal Unicode escape of a @@ -303,8 +306,8 @@ '\r' => EscapeDefaultState::Backslash('r'), '\n' => EscapeDefaultState::Backslash('n'), '\\' | '\'' | '"' => EscapeDefaultState::Backslash(self), - '\x20' ..= '\x7e' => EscapeDefaultState::Char(self), - _ => EscapeDefaultState::Unicode(self.escape_unicode()) + '\x20'..='\x7e' => EscapeDefaultState::Char(self), + _ => EscapeDefaultState::Unicode(self.escape_unicode()), }; EscapeDefault { state: init_state } } @@ -436,30 +439,31 @@ pub fn encode_utf8(self, dst: &mut [u8]) -> &mut str { let code = self as u32; unsafe { - let len = - if code < MAX_ONE_B && !dst.is_empty() { + let len = if code < MAX_ONE_B && !dst.is_empty() { *dst.get_unchecked_mut(0) = code as u8; 1 } else if code < MAX_TWO_B && dst.len() >= 2 { *dst.get_unchecked_mut(0) = (code >> 6 & 0x1F) as u8 | TAG_TWO_B; *dst.get_unchecked_mut(1) = (code & 0x3F) as u8 | TAG_CONT; 2 - } else if code < MAX_THREE_B && dst.len() >= 3 { + } else if code < MAX_THREE_B && dst.len() >= 3 { *dst.get_unchecked_mut(0) = (code >> 12 & 0x0F) as u8 | TAG_THREE_B; - *dst.get_unchecked_mut(1) = (code >> 6 & 0x3F) as u8 | TAG_CONT; + *dst.get_unchecked_mut(1) = (code >> 6 & 0x3F) as u8 | TAG_CONT; *dst.get_unchecked_mut(2) = (code & 0x3F) as u8 | TAG_CONT; 3 } else if dst.len() >= 4 { *dst.get_unchecked_mut(0) = (code >> 18 & 0x07) as u8 | TAG_FOUR_B; *dst.get_unchecked_mut(1) = (code >> 12 & 0x3F) as u8 | TAG_CONT; - *dst.get_unchecked_mut(2) = (code >> 6 & 0x3F) as u8 | TAG_CONT; + *dst.get_unchecked_mut(2) = (code >> 6 & 0x3F) as u8 | TAG_CONT; *dst.get_unchecked_mut(3) = (code & 0x3F) as u8 | TAG_CONT; 4 } else { - panic!("encode_utf8: need {} bytes to encode U+{:X}, but the buffer has {}", + panic!( + "encode_utf8: need {} bytes to encode U+{:X}, but the buffer has {}", from_u32_unchecked(code).len_utf8(), code, - dst.len()) + dst.len(), + ) }; from_utf8_unchecked_mut(dst.get_unchecked_mut(..len)) } @@ -515,15 +519,24 @@ *dst.get_unchecked_mut(1) = 0xDC00 | ((code as u16) & 0x3FF); slice::from_raw_parts_mut(dst.as_mut_ptr(), 2) } else { - panic!("encode_utf16: need {} units to encode U+{:X}, but the buffer has {}", + panic!( + "encode_utf16: need {} units to encode U+{:X}, but the buffer has {}", from_u32_unchecked(code).len_utf16(), code, - dst.len()) + dst.len(), + ) } } } - /// Returns `true` if this `char` is an alphabetic code point, and false if not. + /// Returns `true` if this `char` has the `Alphabetic` property. + /// + /// `Alphabetic` is described in Chapter 4 (Character Properties) of the [Unicode Standard] and + /// specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt /// /// # Examples /// @@ -547,10 +560,14 @@ } } - /// Returns `true` if this `char` is lowercase. + /// Returns `true` if this `char` has the `Lowercase` property. /// - /// 'Lowercase' is defined according to the terms of the Unicode Derived Core - /// Property `Lowercase`. + /// `Lowercase` is described in Chapter 4 (Character Properties) of the [Unicode Standard] and + /// specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt /// /// # Examples /// @@ -575,10 +592,14 @@ } } - /// Returns `true` if this `char` is uppercase. + /// Returns `true` if this `char` has the `Uppercase` property. /// - /// 'Uppercase' is defined according to the terms of the Unicode Derived Core - /// Property `Uppercase`. + /// `Uppercase` is described in Chapter 4 (Character Properties) of the [Unicode Standard] and + /// specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt /// /// # Examples /// @@ -603,10 +624,12 @@ } } - /// Returns `true` if this `char` is whitespace. + /// Returns `true` if this `char` has the `White_Space` property. /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived Core - /// Property `White_Space`. + /// `White_Space` is specified in the [Unicode Character Database][ucd] [`PropList.txt`]. + /// + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`PropList.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt /// /// # Examples /// @@ -630,10 +653,10 @@ } } - /// Returns `true` if this `char` is alphanumeric. + /// Returns `true` if this `char` satisfies either [`is_alphabetic()`] or [`is_numeric()`]. /// - /// 'Alphanumeric'-ness is defined in terms of the Unicode General Categories - /// `Nd`, `Nl`, `No` and the Derived Core Property `Alphabetic`. + /// [`is_alphabetic()`]: #method.is_alphabetic + /// [`is_numeric()`]: #method.is_numeric /// /// # Examples /// @@ -655,10 +678,15 @@ self.is_alphabetic() || self.is_numeric() } - /// Returns `true` if this `char` is a control code point. + /// Returns `true` if this `char` has the general category for control codes. /// - /// 'Control code point' is defined in terms of the Unicode General - /// Category `Cc`. + /// Control codes (code points with the general category of `Cc`) are described in Chapter 4 + /// (Character Properties) of the [Unicode Standard] and specified in the [Unicode Character + /// Database][ucd] [`UnicodeData.txt`]. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`UnicodeData.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt /// /// # Examples /// @@ -675,19 +703,29 @@ general_category::Cc(self) } - /// Returns `true` if this `char` is an extended grapheme character. + /// Returns `true` if this `char` has the `Grapheme_Extend` property. /// - /// 'Extended grapheme character' is defined in terms of the Unicode Shaping and Rendering - /// Category `Grapheme_Extend`. + /// `Grapheme_Extend` is described in [Unicode Standard Annex #29 (Unicode Text + /// Segmentation)][uax29] and specified in the [Unicode Character Database][ucd] + /// [`DerivedCoreProperties.txt`]. + /// + /// [uax29]: https://www.unicode.org/reports/tr29/ + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt #[inline] pub(crate) fn is_grapheme_extended(self) -> bool { derived_property::Grapheme_Extend(self) } - /// Returns `true` if this `char` is numeric. + /// Returns `true` if this `char` has one of the general categories for numbers. /// - /// 'Numeric'-ness is defined in terms of the Unicode General Categories - /// `Nd`, `Nl`, `No`. + /// The general categories for numbers (`Nd` for decimal digits, `Nl` for letter-like numeric + /// characters, and `No` for other numeric characters) are specified in the [Unicode Character + /// Database][ucd] [`UnicodeData.txt`]. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`UnicodeData.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt /// /// # Examples /// @@ -713,25 +751,29 @@ } } - /// Returns an iterator that yields the lowercase equivalent of a `char` - /// as one or more `char`s. + /// Returns an iterator that yields the lowercase mapping of this `char` as one or more + /// `char`s. /// - /// If a character does not have a lowercase equivalent, the same character - /// will be returned back by the iterator. + /// If this `char` does not have a lowercase mapping, the iterator yields the same `char`. /// - /// This performs complex unconditional mappings with no tailoring: it maps - /// one Unicode character to its lowercase equivalent according to the - /// [Unicode database] and the additional complex mappings - /// [`SpecialCasing.txt`]. Conditional mappings (based on context or - /// language) are not considered here. + /// If this `char` has a one-to-one lowercase mapping given by the [Unicode Character + /// Database][ucd] [`UnicodeData.txt`], the iterator yields that `char`. /// - /// For a full reference, see [here][reference]. + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`UnicodeData.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt /// - /// [Unicode database]: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt + /// If this `char` requires special considerations (e.g. multiple `char`s) the iterator yields + /// the `char`(s) given by [`SpecialCasing.txt`]. /// - /// [`SpecialCasing.txt`]: ftp://ftp.unicode.org/Public/UNIDATA/SpecialCasing.txt + /// [`SpecialCasing.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt /// - /// [reference]: http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G33992 + /// This operation performs an unconditional mapping without tailoring. That is, the conversion + /// is independent of context and language. + /// + /// In the [Unicode Standard], Chapter 4 (Character Properties) discusses case mapping in + /// general and Chapter 3 (Conformance) discusses the default algorithm for case conversion. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ /// /// # Examples /// @@ -774,25 +816,29 @@ ToLowercase(CaseMappingIter::new(conversions::to_lower(self))) } - /// Returns an iterator that yields the uppercase equivalent of a `char` - /// as one or more `char`s. + /// Returns an iterator that yields the uppercase mapping of this `char` as one or more + /// `char`s. /// - /// If a character does not have an uppercase equivalent, the same character - /// will be returned back by the iterator. + /// If this `char` does not have a uppercase mapping, the iterator yields the same `char`. /// - /// This performs complex unconditional mappings with no tailoring: it maps - /// one Unicode character to its uppercase equivalent according to the - /// [Unicode database] and the additional complex mappings - /// [`SpecialCasing.txt`]. Conditional mappings (based on context or - /// language) are not considered here. + /// If this `char` has a one-to-one uppercase mapping given by the [Unicode Character + /// Database][ucd] [`UnicodeData.txt`], the iterator yields that `char`. /// - /// For a full reference, see [here][reference]. + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`UnicodeData.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt /// - /// [Unicode database]: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt + /// If this `char` requires special considerations (e.g. multiple `char`s) the iterator yields + /// the `char`(s) given by [`SpecialCasing.txt`]. /// - /// [`SpecialCasing.txt`]: ftp://ftp.unicode.org/Public/UNIDATA/SpecialCasing.txt + /// [`SpecialCasing.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt /// - /// [reference]: http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G33992 + /// This operation performs an unconditional mapping without tailoring. That is, the conversion + /// is independent of context and language. + /// + /// In the [Unicode Standard], Chapter 4 (Character Properties) discusses case mapping in + /// general and Chapter 3 (Conformance) discusses the default algorithm for case conversion. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ /// /// # Examples ///
diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index 6bdae1b..14d947c 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs
@@ -135,7 +135,6 @@ /// Derive macro generating an impl of the trait `Clone`. #[rustc_builtin_macro] -#[cfg_attr(bootstrap, rustc_macro_transparency = "semitransparent")] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow_internal_unstable(core_intrinsics, derive_clone_copy)] pub macro Clone($item:item) { /* compiler built-in */ }
diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index fc7329f..1ac5129 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs
@@ -210,9 +210,8 @@ /// Derive macro generating an impl of the trait `PartialEq`. #[rustc_builtin_macro] -#[cfg_attr(bootstrap, rustc_macro_transparency = "semitransparent")] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics)] +#[allow_internal_unstable(core_intrinsics, structural_match)] pub macro PartialEq($item:item) { /* compiler built-in */ } /// Trait for equality comparisons which are [equivalence relations]( @@ -273,9 +272,8 @@ /// Derive macro generating an impl of the trait `Eq`. #[rustc_builtin_macro] -#[cfg_attr(bootstrap, rustc_macro_transparency = "semitransparent")] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics, derive_eq)] +#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match)] pub macro Eq($item:item) { /* compiler built-in */ } // FIXME: this struct is used solely by #[derive] to @@ -624,7 +622,6 @@ /// Derive macro generating an impl of the trait `Ord`. #[rustc_builtin_macro] -#[cfg_attr(bootstrap, rustc_macro_transparency = "semitransparent")] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow_internal_unstable(core_intrinsics)] pub macro Ord($item:item) { /* compiler built-in */ } @@ -873,7 +870,6 @@ /// Derive macro generating an impl of the trait `PartialOrd`. #[rustc_builtin_macro] -#[cfg_attr(bootstrap, rustc_macro_transparency = "semitransparent")] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow_internal_unstable(core_intrinsics)] pub macro PartialOrd($item:item) { /* compiler built-in */ }
diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 06f2b7b..3cd2337 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs
@@ -554,6 +554,18 @@ fn from(t: T) -> T { t } } +/// **Stability note:** This impl does not yet exist, but we are +/// "reserving space" to add it in the future. See +/// [rust-lang/rust#64715][#64715] for details. +/// +/// [#64715]: https://github.com/rust-lang/rust/issues/64715 +#[stable(feature = "convert_infallible", since = "1.34.0")] +#[cfg(not(bootstrap))] +#[rustc_reservation_impl="permitting this impl would forbid us from adding \ +`impl<T> From<!> for T` later; see rust-lang/rust#64715 for details"] +impl<T> From<!> for T { + fn from(t: !) -> T { t } +} // TryFrom implies TryInto #[stable(feature = "try_from", since = "1.34.0")]
diff --git a/src/libcore/default.rs b/src/libcore/default.rs index 806d478..1aadc77 100644 --- a/src/libcore/default.rs +++ b/src/libcore/default.rs
@@ -117,7 +117,6 @@ /// Derive macro generating an impl of the trait `Default`. #[rustc_builtin_macro] -#[cfg_attr(bootstrap, rustc_macro_transparency = "semitransparent")] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow_internal_unstable(core_intrinsics)] pub macro Default($item:item) { /* compiler built-in */ }
diff --git a/src/libcore/ffi.rs b/src/libcore/ffi.rs index eda0e7c..569c667 100644 --- a/src/libcore/ffi.rs +++ b/src/libcore/ffi.rs
@@ -18,8 +18,13 @@ /// stabilized, it is recommended to use a newtype wrapper around an empty /// byte array. See the [Nomicon] for details. /// +/// One could use `std::os::raw::c_void` if they want to support old Rust +/// compiler down to 1.1.0. After Rust 1.30.0, it was re-exported by +/// this definition. For more information, please read [RFC 2521]. +/// /// [pointer]: ../../std/primitive.pointer.html /// [Nomicon]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs +/// [RFC 2521]: https://github.com/rust-lang/rfcs/blob/master/text/2521-c_void-reunification.md // N.B., for LLVM to recognize the void pointer type and by extension // functions like malloc(), we need to have it represented as i8* in // LLVM bitcode. The enum used here ensures this and prevents misuse @@ -29,7 +34,7 @@ // would be uninhabited and at least dereferencing such pointers would // be UB. #[repr(u8)] -#[stable(feature = "raw_os", since = "1.1.0")] +#[stable(feature = "core_c_void", since = "1.30.0")] pub enum c_void { #[unstable(feature = "c_void_variant", reason = "temporary implementation detail", issue = "0")] @@ -49,8 +54,10 @@ /// Basic implementation of a `va_list`. // The name is WIP, using `VaListImpl` for now. #[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), - not(target_arch = "x86_64"), not(target_arch = "asmjs")), + not(target_arch = "x86_64")), all(target_arch = "aarch64", target_os = "ios"), + target_arch = "wasm32", + target_arch = "asmjs", windows))] #[repr(transparent)] #[unstable(feature = "c_variadic", @@ -67,8 +74,10 @@ } #[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), - not(target_arch = "x86_64"), not(target_arch = "asmjs")), + not(target_arch = "x86_64")), all(target_arch = "aarch64", target_os = "ios"), + target_arch = "wasm32", + target_arch = "asmjs", windows))] #[unstable(feature = "c_variadic", reason = "the `c_variadic` feature has not been properly tested on \ @@ -137,38 +146,6 @@ _marker: PhantomData<&'f mut &'f c_void>, } -/// asm.js ABI implementation of a `va_list`. -// asm.js uses the PNaCl ABI, which specifies that a `va_list` is -// an array of 4 32-bit integers, according to the old PNaCl docs at -// https://web.archive.org/web/20130518054430/https://www.chromium.org/nativeclient/pnacl/bitcode-abi#TOC-Derived-Types -// and clang does the same in `CreatePNaClABIBuiltinVaListDecl` from `lib/AST/ASTContext.cpp` -#[cfg(all(target_arch = "asmjs", not(windows)))] -#[repr(C)] -#[unstable(feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930")] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - inner: [crate::mem::MaybeUninit<i32>; 4], - _marker: PhantomData<&'f mut &'f c_void>, -} - -#[cfg(all(target_arch = "asmjs", not(windows)))] -#[unstable(feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930")] -impl<'f> fmt::Debug for VaListImpl<'f> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - unsafe { - write!(f, "va_list* [{:#x}, {:#x}, {:#x}, {:#x}]", - self.inner[0].read(), self.inner[1].read(), - self.inner[2].read(), self.inner[3].read()) - } - } -} - /// A wrapper for a `va_list` #[repr(transparent)] #[derive(Debug)] @@ -178,14 +155,18 @@ issue = "44930")] pub struct VaList<'a, 'f: 'a> { #[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), - not(target_arch = "x86_64"), not(target_arch = "asmjs")), + not(target_arch = "x86_64")), all(target_arch = "aarch64", target_os = "ios"), + target_arch = "wasm32", + target_arch = "asmjs", windows))] inner: VaListImpl<'f>, #[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", - target_arch = "x86_64", target_arch = "asmjs"), + target_arch = "x86_64"), any(not(target_arch = "aarch64"), not(target_os = "ios")), + not(target_arch = "wasm32"), + not(target_arch = "asmjs"), not(windows)))] inner: &'a mut VaListImpl<'f>, @@ -193,8 +174,10 @@ } #[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), - not(target_arch = "x86_64"), not(target_arch = "asmjs")), + not(target_arch = "x86_64")), all(target_arch = "aarch64", target_os = "ios"), + target_arch = "wasm32", + target_arch = "asmjs", windows))] #[unstable(feature = "c_variadic", reason = "the `c_variadic` feature has not been properly tested on \ @@ -212,8 +195,10 @@ } #[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", - target_arch = "x86_64", target_arch = "asmjs"), + target_arch = "x86_64"), any(not(target_arch = "aarch64"), not(target_os = "ios")), + not(target_arch = "wasm32"), + not(target_arch = "asmjs"), not(windows)))] #[unstable(feature = "c_variadic", reason = "the `c_variadic` feature has not been properly tested on \
diff --git a/src/libcore/fmt/builders.rs b/src/libcore/fmt/builders.rs index 15ce227..e6e3454 100644 --- a/src/libcore/fmt/builders.rs +++ b/src/libcore/fmt/builders.rs
@@ -775,10 +775,10 @@ reason = "recently added", issue = "62482")] pub fn key(&mut self, key: &dyn fmt::Debug) -> &mut DebugMap<'a, 'b> { - assert!(!self.has_key, "attempted to begin a new map entry \ - without completing the previous one"); - self.result = self.result.and_then(|_| { + assert!(!self.has_key, "attempted to begin a new map entry \ + without completing the previous one"); + if self.is_pretty() { if !self.has_fields { self.fmt.write_str("\n")?; @@ -839,9 +839,9 @@ reason = "recently added", issue = "62482")] pub fn value(&mut self, value: &dyn fmt::Debug) -> &mut DebugMap<'a, 'b> { - assert!(self.has_key, "attempted to format a map value before its key"); - self.result = self.result.and_then(|_| { + assert!(self.has_key, "attempted to format a map value before its key"); + if self.is_pretty() { let mut slot = None; let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut self.state); @@ -924,9 +924,11 @@ /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn finish(&mut self) -> fmt::Result { - assert!(!self.has_key, "attempted to finish a map with a partial entry"); + self.result.and_then(|_| { + assert!(!self.has_key, "attempted to finish a map with a partial entry"); - self.result.and_then(|_| self.fmt.write_str("}")) + self.fmt.write_str("}") + }) } fn is_pretty(&self) -> bool {
diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 65e2f8b..0e83a28 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs
@@ -108,10 +108,10 @@ /// [`io::Write`]: ../../std/io/trait.Write.html #[stable(feature = "rust1", since = "1.0.0")] pub trait Write { - /// Writes a slice of bytes into this writer, returning whether the write + /// Writes a string slice into this writer, returning whether the write /// succeeded. /// - /// This method can only succeed if the entire byte slice was successfully + /// This method can only succeed if the entire string slice was successfully /// written, and this method will not return until all data has been /// written or an error occurs. /// @@ -518,8 +518,7 @@ label="`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{Debug}`", )] #[doc(alias = "{:?}")] -#[cfg_attr(bootstrap, lang = "debug_trait")] -#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "debug_trait")] +#[rustc_diagnostic_item = "debug_trait"] pub trait Debug { /// Formats the value using the given formatter. /// @@ -550,7 +549,6 @@ pub(crate) mod macros { /// Derive macro generating an impl of the trait `Debug`. #[rustc_builtin_macro] - #[cfg_attr(bootstrap, rustc_macro_transparency = "semitransparent")] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow_internal_unstable(core_intrinsics)] pub macro Debug($item:item) { /* compiler built-in */ } @@ -1534,12 +1532,10 @@ /// } /// } /// - /// fn main() { - /// assert_eq!(&format!("{:<}", Foo), "left"); - /// assert_eq!(&format!("{:>}", Foo), "right"); - /// assert_eq!(&format!("{:^}", Foo), "center"); - /// assert_eq!(&format!("{}", Foo), "into the void"); - /// } + /// assert_eq!(&format!("{:<}", Foo), "left"); + /// assert_eq!(&format!("{:>}", Foo), "right"); + /// assert_eq!(&format!("{:^}", Foo), "center"); + /// assert_eq!(&format!("{}", Foo), "into the void"); /// ``` #[stable(feature = "fmt_flags_align", since = "1.28.0")] pub fn align(&self) -> Option<Alignment> { @@ -2029,7 +2025,7 @@ if f.alternate() { f.flags |= 1 << (FlagV1::SignAwareZeroPad as u32); - if let None = f.width { + if f.width.is_none() { f.width = Some(((mem::size_of::<usize>() * 8) / 4) + 2); } }
diff --git a/src/libcore/hash/mod.rs b/src/libcore/hash/mod.rs index aaaa6f9..020e085 100644 --- a/src/libcore/hash/mod.rs +++ b/src/libcore/hash/mod.rs
@@ -202,7 +202,6 @@ pub(crate) mod macros { /// Derive macro generating an impl of the trait `Hash`. #[rustc_builtin_macro] - #[cfg_attr(bootstrap, rustc_macro_transparency = "semitransparent")] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow_internal_unstable(core_intrinsics)] pub macro Hash($item:item) { /* compiler built-in */ }
diff --git a/src/libcore/hint.rs b/src/libcore/hint.rs index ee4be6c..368a2f1 100644 --- a/src/libcore/hint.rs +++ b/src/libcore/hint.rs
@@ -114,24 +114,8 @@ // this. LLVM's intepretation of inline assembly is that it's, well, a black // box. This isn't the greatest implementation since it probably deoptimizes // more than we want, but it's so far good enough. - #[cfg(not(any( - target_arch = "asmjs", - all( - target_arch = "wasm32", - target_os = "emscripten" - ) - )))] unsafe { asm!("" : : "r"(&dummy)); return dummy; } - - // Not all platforms support inline assembly so try to do something without - // inline assembly which in theory still hinders at least some optimizations - // on those targets. This is the "best effort" scenario. - unsafe { - let ret = crate::ptr::read_volatile(&dummy); - crate::mem::forget(dummy); - ret - } }
diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 905375e..3db85d0 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs
@@ -696,6 +696,10 @@ /// This will statically either panic, or do nothing. pub fn panic_if_uninhabited<T>(); + /// Gets a reference to a static `Location` indicating where it was called. + #[cfg(not(bootstrap))] + pub fn caller_location() -> &'static crate::panic::Location<'static>; + /// Creates a value initialized to zero. /// /// `init` is unsafe because it returns a zeroed-out datum, @@ -874,6 +878,7 @@ /// // the original inner type (`&i32`) to the converted inner type /// // (`Option<&i32>`), so read the nomicon pages linked above. /// let v_from_raw = unsafe { + // FIXME Update this when vec_into_raw_parts is stabilized /// // Ensure the original vector is not dropped. /// let mut v_clone = std::mem::ManuallyDrop::new(v_clone); /// Vec::from_raw_parts(v_clone.as_mut_ptr() as *mut Option<&i32>, @@ -1299,38 +1304,16 @@ /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_add` method. For example, /// [`std::u32::wrapping_add`](../../std/primitive.u32.html#method.wrapping_add) - #[cfg(bootstrap)] - pub fn overflowing_add<T>(a: T, b: T) -> T; - /// Returns (a - b) mod 2<sup>N</sup>, where N is the width of T in bits. - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `wrapping_sub` method. For example, - /// [`std::u32::wrapping_sub`](../../std/primitive.u32.html#method.wrapping_sub) - #[cfg(bootstrap)] - pub fn overflowing_sub<T>(a: T, b: T) -> T; - /// Returns (a * b) mod 2<sup>N</sup>, where N is the width of T in bits. - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `wrapping_mul` method. For example, - /// [`std::u32::wrapping_mul`](../../std/primitive.u32.html#method.wrapping_mul) - #[cfg(bootstrap)] - pub fn overflowing_mul<T>(a: T, b: T) -> T; - - /// Returns (a + b) mod 2<sup>N</sup>, where N is the width of T in bits. - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `wrapping_add` method. For example, - /// [`std::u32::wrapping_add`](../../std/primitive.u32.html#method.wrapping_add) - #[cfg(not(bootstrap))] pub fn wrapping_add<T>(a: T, b: T) -> T; /// Returns (a - b) mod 2<sup>N</sup>, where N is the width of T in bits. /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_sub` method. For example, /// [`std::u32::wrapping_sub`](../../std/primitive.u32.html#method.wrapping_sub) - #[cfg(not(bootstrap))] pub fn wrapping_sub<T>(a: T, b: T) -> T; /// Returns (a * b) mod 2<sup>N</sup>, where N is the width of T in bits. /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_mul` method. For example, /// [`std::u32::wrapping_mul`](../../std/primitive.u32.html#method.wrapping_mul) - #[cfg(not(bootstrap))] pub fn wrapping_mul<T>(a: T, b: T) -> T; /// Computes `a + b`, while saturating at numeric bounds. @@ -1361,6 +1344,10 @@ /// Emits a `!nontemporal` store according to LLVM (see their docs). /// Probably will never become stable. pub fn nontemporal_store<T>(ptr: *mut T, val: T); + + /// See documentation of `<*const T>::offset_from` for details. + #[cfg(not(bootstrap))] + pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize; } // Some functions are defined here because they accidentally got made
diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index aba8e84..fac6ff0 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs
@@ -118,26 +118,16 @@ //! //! let mut counter = Counter::new(); //! -//! let x = counter.next().unwrap(); -//! println!("{}", x); -//! -//! let x = counter.next().unwrap(); -//! println!("{}", x); -//! -//! let x = counter.next().unwrap(); -//! println!("{}", x); -//! -//! let x = counter.next().unwrap(); -//! println!("{}", x); -//! -//! let x = counter.next().unwrap(); -//! println!("{}", x); +//! assert_eq!(counter.next(), Some(1)); +//! assert_eq!(counter.next(), Some(2)); +//! assert_eq!(counter.next(), Some(3)); +//! assert_eq!(counter.next(), Some(4)); +//! assert_eq!(counter.next(), Some(5)); +//! assert_eq!(counter.next(), None); //! ``` //! -//! This will print `1` through `5`, each on their own line. -//! -//! Calling `next()` this way gets repetitive. Rust has a construct which can -//! call `next()` on your iterator, until it reaches `None`. Let's go over that +//! Calling [`next`] this way gets repetitive. Rust has a construct which can +//! call [`next`] on your iterator, until it reaches `None`. Let's go over that //! next. //! //! Also note that `Iterator` provides a default implementation of methods such as `nth` and `fold` @@ -253,20 +243,23 @@ //! ``` //! //! The idiomatic way to write a [`map`] for its side effects is to use a -//! `for` loop instead: +//! `for` loop or call the [`for_each`] method: //! //! ``` //! let v = vec![1, 2, 3, 4, 5]; //! +//! v.iter().for_each(|x| println!("{}", x)); +//! // or //! for x in &v { //! println!("{}", x); //! } //! ``` //! //! [`map`]: trait.Iterator.html#method.map +//! [`for_each`]: trait.Iterator.html#method.for_each //! -//! The two most common ways to evaluate an iterator are to use a `for` loop -//! like this, or using the [`collect`] method to produce a new collection. +//! Another common way to evaluate an iterator is to use the [`collect`] +//! method to produce a new collection. //! //! [`collect`]: trait.Iterator.html#method.collect //!
diff --git a/src/libcore/iter/traits/collect.rs b/src/libcore/iter/traits/collect.rs index 2543913..00a8641 100644 --- a/src/libcore/iter/traits/collect.rs +++ b/src/libcore/iter/traits/collect.rs
@@ -167,7 +167,7 @@ /// // and we'll implement IntoIterator /// impl IntoIterator for MyCollection { /// type Item = i32; -/// type IntoIter = ::std::vec::IntoIter<Self::Item>; +/// type IntoIter = std::vec::IntoIter<Self::Item>; /// /// fn into_iter(self) -> Self::IntoIter { /// self.0.into_iter()
diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs index da49223..7ffc8b3 100644 --- a/src/libcore/iter/traits/iterator.rs +++ b/src/libcore/iter/traits/iterator.rs
@@ -384,6 +384,9 @@ /// /// In other words, it links two iterators together, in a chain. 🔗 /// + /// [`once`] is commonly used to adapt a single value into a chain of + /// other kinds of iteration. + /// /// # Examples /// /// Basic usage: @@ -408,9 +411,6 @@ /// [`Iterator`] itself. For example, slices (`&[T]`) implement /// [`IntoIterator`], and so can be passed to `chain()` directly: /// - /// [`IntoIterator`]: trait.IntoIterator.html - /// [`Iterator`]: trait.Iterator.html - /// /// ``` /// let s1 = &[1, 2, 3]; /// let s2 = &[4, 5, 6]; @@ -425,6 +425,21 @@ /// assert_eq!(iter.next(), Some(&6)); /// assert_eq!(iter.next(), None); /// ``` + /// + /// If you work with Windows API, you may wish to convert [`OsStr`] to `Vec<u16>`: + /// + /// ``` + /// #[cfg(windows)] + /// fn os_str_to_utf16(s: &std::ffi::OsStr) -> Vec<u16> { + /// use std::os::windows::ffi::OsStrExt; + /// s.encode_wide().chain(std::iter::once(0)).collect() + /// } + /// ``` + /// + /// [`once`]: fn.once.html + /// [`Iterator`]: trait.Iterator.html + /// [`IntoIterator`]: trait.IntoIterator.html + /// [`OsStr`]: ../../std/ffi/struct.OsStr.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn chain<U>(self, other: U) -> Chain<Self, U::IntoIter> where @@ -1859,14 +1874,13 @@ Self: Sized, F: FnMut(Self::Item) -> bool { #[inline] - fn check<T>(mut f: impl FnMut(T) -> bool) -> impl FnMut(T) -> LoopState<(), ()> { - move |x| { + fn check<T>(mut f: impl FnMut(T) -> bool) -> impl FnMut((), T) -> LoopState<(), ()> { + move |(), x| { if f(x) { LoopState::Continue(()) } else { LoopState::Break(()) } } } - - self.try_for_each(check(f)) == LoopState::Continue(()) + self.try_fold((), check(f)) == LoopState::Continue(()) } /// Tests if any element of the iterator matches a predicate. @@ -1913,14 +1927,14 @@ F: FnMut(Self::Item) -> bool { #[inline] - fn check<T>(mut f: impl FnMut(T) -> bool) -> impl FnMut(T) -> LoopState<(), ()> { - move |x| { + fn check<T>(mut f: impl FnMut(T) -> bool) -> impl FnMut((), T) -> LoopState<(), ()> { + move |(), x| { if f(x) { LoopState::Break(()) } else { LoopState::Continue(()) } } } - self.try_for_each(check(f)) == LoopState::Break(()) + self.try_fold((), check(f)) == LoopState::Break(()) } /// Searches for an element of an iterator that satisfies a predicate. @@ -1972,14 +1986,16 @@ P: FnMut(&Self::Item) -> bool, { #[inline] - fn check<T>(mut predicate: impl FnMut(&T) -> bool) -> impl FnMut(T) -> LoopState<(), T> { - move |x| { + fn check<T>( + mut predicate: impl FnMut(&T) -> bool + ) -> impl FnMut((), T) -> LoopState<(), T> { + move |(), x| { if predicate(&x) { LoopState::Break(x) } else { LoopState::Continue(()) } } } - self.try_for_each(check(predicate)).break_value() + self.try_fold((), check(predicate)).break_value() } /// Applies function to the elements of iterator and returns @@ -2004,14 +2020,14 @@ F: FnMut(Self::Item) -> Option<B>, { #[inline] - fn check<T, B>(mut f: impl FnMut(T) -> Option<B>) -> impl FnMut(T) -> LoopState<(), B> { - move |x| match f(x) { + fn check<T, B>(mut f: impl FnMut(T) -> Option<B>) -> impl FnMut((), T) -> LoopState<(), B> { + move |(), x| match f(x) { Some(x) => LoopState::Break(x), None => LoopState::Continue(()), } } - self.try_for_each(check(f)).break_value() + self.try_fold((), check(f)).break_value() } /// Searches for an element in an iterator, returning its index. @@ -2581,7 +2597,7 @@ /// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| (x * x).cmp(&y)), Ordering::Equal); /// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| (2 * x).cmp(&y)), Ordering::Greater); /// ``` - #[unstable(feature = "iter_order_by", issue = "0")] + #[unstable(feature = "iter_order_by", issue = "64295")] fn cmp_by<I, F>(mut self, other: I, mut cmp: F) -> Ordering where Self: Sized, @@ -2664,7 +2680,7 @@ /// Some(Ordering::Greater) /// ); /// ``` - #[unstable(feature = "iter_order_by", issue = "0")] + #[unstable(feature = "iter_order_by", issue = "64295")] fn partial_cmp_by<I, F>(mut self, other: I, mut partial_cmp: F) -> Option<Ordering> where Self: Sized, @@ -2729,7 +2745,7 @@ /// /// assert!(xs.iter().eq_by(&ys, |&x, &y| x * x == y)); /// ``` - #[unstable(feature = "iter_order_by", issue = "0")] + #[unstable(feature = "iter_order_by", issue = "64295")] fn eq_by<I, F>(mut self, other: I, mut eq: F) -> bool where Self: Sized,
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 8221df5..1b67b05 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs
@@ -87,7 +87,6 @@ #![feature(link_llvm_intrinsics)] #![feature(never_type)] #![feature(nll)] -#![cfg_attr(bootstrap, feature(bind_by_move_pattern_guards))] #![feature(exhaustive_patterns)] #![feature(no_core)] #![feature(on_unimplemented)] @@ -120,24 +119,21 @@ #![feature(rtm_target_feature)] #![feature(f16c_target_feature)] #![feature(hexagon_target_feature)] -#![cfg_attr(bootstrap, feature(const_slice_len))] -#![cfg_attr(bootstrap, feature(const_str_as_bytes))] -#![cfg_attr(bootstrap, feature(const_str_len))] #![feature(const_int_conversion)] #![feature(const_transmute)] -#![feature(non_exhaustive)] +#![cfg_attr(bootstrap, feature(non_exhaustive))] #![feature(structural_match)] #![feature(abi_unadjusted)] #![feature(adx_target_feature)] #![feature(maybe_uninit_slice)] #![feature(external_doc)] -#![feature(mem_take)] #![feature(associated_type_bounds)] #[prelude_import] #[allow(unused)] use prelude::v1::*; +#[cfg(not(test))] // See #65860 #[macro_use] mod macros; @@ -185,10 +181,14 @@ /* Core language traits */ +#[cfg(not(test))] // See #65860 pub mod marker; pub mod ops; +#[cfg(not(test))] // See #65860 pub mod cmp; +#[cfg(not(test))] // See #65860 pub mod clone; +#[cfg(not(test))] // See #65860 pub mod default; pub mod convert; pub mod borrow; @@ -196,6 +196,7 @@ /* Core types and methods on primitives */ pub mod any; +#[cfg(not(test))] // See #65860 pub mod array; pub mod ascii; pub mod sync; @@ -203,7 +204,9 @@ pub mod char; pub mod panic; pub mod panicking; +#[cfg(not(test))] // See #65860 pub mod pin; +#[cfg(not(test))] // See #65860 pub mod iter; pub mod option; pub mod raw; @@ -211,14 +214,18 @@ pub mod ffi; pub mod slice; +#[cfg(not(test))] // See #65860 pub mod str; +#[cfg(not(test))] // See #65860 pub mod hash; +#[cfg(not(test))] // See #65860 pub mod fmt; pub mod time; pub mod unicode; /* Async */ +#[cfg(not(test))] // See #65860 pub mod future; pub mod task;
diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index c6f5fb0..8ccd31c 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs
@@ -1,8 +1,9 @@ /// Panics the current thread. /// /// For details, see `std::macros`. +#[cfg(bootstrap)] #[macro_export] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(core_panic, panic_internals)] #[stable(feature = "core", since = "1.6.0")] macro_rules! panic { () => ( @@ -20,6 +21,38 @@ }); } +/// Panics the current thread. +/// +/// For details, see `std::macros`. +#[cfg(not(bootstrap))] +#[macro_export] +#[allow_internal_unstable(core_panic, panic_internals)] +#[stable(feature = "core", since = "1.6.0")] +macro_rules! panic { + () => ( + $crate::panic!("explicit panic") + ); + ($msg:expr) => ({ + const LOC: &$crate::panic::Location<'_> = &$crate::panic::Location::internal_constructor( + $crate::file!(), + $crate::line!(), + $crate::column!(), + ); + $crate::panicking::panic($msg, LOC) + }); + ($msg:expr,) => ( + $crate::panic!($msg) + ); + ($fmt:expr, $($arg:tt)+) => ({ + const LOC: &$crate::panic::Location<'_> = &$crate::panic::Location::internal_constructor( + $crate::file!(), + $crate::line!(), + $crate::column!(), + ); + $crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+), LOC) + }); +} + /// Asserts that two expressions are equal to each other (using [`PartialEq`]). /// /// On panic, this macro will print the values of the expressions with their @@ -238,6 +271,33 @@ ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_ne!($($arg)*); }) } +/// Returns whether the given expression matches any of the given patterns. +/// +/// Like in a `match` expression, the pattern can be optionally followed by `if` +/// and a guard expression that has access to names bound by the pattern. +/// +/// # Examples +/// +/// ``` +/// #![feature(matches_macro)] +/// +/// let foo = 'f'; +/// assert!(matches!(foo, 'A'..='Z' | 'a'..='z')); +/// +/// let bar = Some(4); +/// assert!(matches!(bar, Some(x) if x > 2)); +/// ``` +#[macro_export] +#[unstable(feature = "matches_macro", issue = "65721")] +macro_rules! matches { + ($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )?) => { + match $expression { + $( $pattern )|+ $( if $guard )? => true, + _ => false + } + } +} + /// Unwraps a result or propagates its error. /// /// The `?` operator was added to replace `try!` and should be used instead. @@ -465,7 +525,7 @@ /// The unsafe counterpart of this macro is the [`unreachable_unchecked`] function, which /// will cause undefined behavior if the code is reached. /// -/// [`panic!`]: ../std/macro.panic.html +/// [`panic!`]: ../std/macro.panic.html /// [`unreachable_unchecked`]: ../std/hint/fn.unreachable_unchecked.html /// [`std::hint`]: ../std/hint/index.html /// @@ -474,6 +534,7 @@ /// This will always [`panic!`] /// /// [`panic!`]: ../std/macro.panic.html +/// /// # Examples /// /// Match arms: @@ -519,15 +580,20 @@ }); } -/// Indicates unfinished code. +/// Indicates unfinished code by panicking with a message of "not yet implemented". /// -/// This can be useful if you are prototyping and are just looking to have your -/// code type-check, or if you're implementing a trait that requires multiple -/// methods, and you're only planning on using one of them. +/// This allows the your code to type-check, which is useful if you are prototyping or +/// implementing a trait that requires multiple methods which you don't plan of using all of. +/// +/// There is no difference between `unimplemented!` and `todo!` apart from the +/// name. /// /// # Panics /// -/// This will always [panic!](macro.panic.html) +/// This will always [panic!](macro.panic.html) because `unimplemented!` is just a +/// shorthand for `panic!` with a fixed, specific message. +/// +/// Like `panic!`, this macro has a second form for displaying custom values. /// /// # Examples /// @@ -535,38 +601,53 @@ /// /// ``` /// trait Foo { -/// fn bar(&self); +/// fn bar(&self) -> u8; /// fn baz(&self); +/// fn qux(&self) -> Result<u64, ()>; /// } /// ``` /// -/// We want to implement `Foo` on one of our types, but we also want to work on -/// just `bar()` first. In order for our code to compile, we need to implement -/// `baz()`, so we can use `unimplemented!`: +/// We want to implement `Foo` for 'MyStruct', but so far we only know how to +/// implement the `bar()` function. `baz()` and `qux()` will still need to be defined +/// in our implementation of `Foo`, but we can use `unimplemented!` in their definitions +/// to allow our code to compile. +/// +/// In the meantime, we want to have our program stop running once these +/// unimplemented functions are reached. /// /// ``` /// # trait Foo { -/// # fn bar(&self); +/// # fn bar(&self) -> u8; /// # fn baz(&self); +/// # fn qux(&self) -> Result<u64, ()>; /// # } /// struct MyStruct; /// /// impl Foo for MyStruct { -/// fn bar(&self) { -/// // implementation goes here +/// fn bar(&self) -> u8 { +/// 1 + 1 /// } /// /// fn baz(&self) { -/// // let's not worry about implementing baz() for now +/// // We aren't sure how to even start writing baz yet, +/// // so we have no logic here at all. +/// // This will display "thread 'main' panicked at 'not yet implemented'". /// unimplemented!(); /// } +/// +/// fn qux(&self) -> Result<u64, ()> { +/// let n = self.bar(); +/// // We have some logic here, +/// // so we can use unimplemented! to display what we have so far. +/// // This will display: +/// // "thread 'main' panicked at 'not yet implemented: we need to divide by 2'". +/// unimplemented!("we need to divide by {}", n); +/// } /// } /// /// fn main() { /// let s = MyStruct; /// s.bar(); -/// -/// // we aren't even using baz() yet, so this is fine. /// } /// ``` #[macro_export] @@ -579,8 +660,10 @@ /// Indicates unfinished code. /// /// This can be useful if you are prototyping and are just looking to have your -/// code typecheck. `todo!` works exactly like `unimplemented!`. The only -/// difference between the two macros is the name. +/// code typecheck. +/// +/// There is no difference between `unimplemented!` and `todo!` apart from the +/// name. /// /// # Panics /// @@ -602,8 +685,6 @@ /// `baz()`, so we can use `todo!`: /// /// ``` -/// #![feature(todo_macro)] -/// /// # trait Foo { /// # fn bar(&self); /// # fn baz(&self); @@ -629,7 +710,7 @@ /// } /// ``` #[macro_export] -#[unstable(feature = "todo_macro", issue = "59277")] +#[stable(feature = "todo_macro", since = "1.39.0")] macro_rules! todo { () => (panic!("not yet implemented")); ($($arg:tt)+) => (panic!("not yet implemented: {}", $crate::format_args!($($arg)+))); @@ -1236,10 +1317,8 @@ pub macro test($item:item) { /* compiler built-in */ } /// Attribute macro applied to a function to turn it into a benchmark test. - #[cfg_attr(not(bootstrap), unstable(soft, feature = "test", issue = "50297", - reason = "`bench` is a part of custom test frameworks which are unstable"))] - #[cfg_attr(bootstrap, unstable(feature = "test", issue = "50297", - reason = "`bench` is a part of custom test frameworks which are unstable"))] + #[unstable(soft, feature = "test", issue = "50297", + reason = "`bench` is a part of custom test frameworks which are unstable")] #[allow_internal_unstable(test, rustc_attrs)] #[rustc_builtin_macro] pub macro bench($item:item) { /* compiler built-in */ }
diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index a2cfb32..a25573f 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs
@@ -126,6 +126,85 @@ // Empty. } +/// Required trait for constants used in pattern matches. +/// +/// Any type that derives `PartialEq` automatically implements this trait, +/// *regardless* of whether its type-parameters implement `Eq`. +/// +/// If a `const` item contains some type that does not implement this trait, +/// then that type either (1.) does not implement `PartialEq` (which means the +/// constant will not provide that comparison method, which code generation +/// assumes is available), or (2.) it implements *its own* version of +/// `PartialEq` (which we assume does not conform to a structural-equality +/// comparison). +/// +/// In either of the two scenarios above, we reject usage of such a constant in +/// a pattern match. +/// +/// See also the [structural match RFC][RFC1445], and [issue 63438][] which +/// motivated migrating from attribute-based design to this trait. +/// +/// [RFC1445]: https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md +/// [issue 63438]: https://github.com/rust-lang/rust/issues/63438 +#[cfg(not(bootstrap))] +#[unstable(feature = "structural_match", issue = "31434")] +#[rustc_on_unimplemented(message="the type `{Self}` does not `#[derive(PartialEq)]`")] +#[lang = "structural_peq"] +pub trait StructuralPartialEq { + // Empty. +} + +/// Required trait for constants used in pattern matches. +/// +/// Any type that derives `Eq` automatically implements this trait, *regardless* +/// of whether its type-parameters implement `Eq`. +/// +/// This is a hack to workaround a limitation in our type-system. +/// +/// Background: +/// +/// We want to require that types of consts used in pattern matches +/// have the attribute `#[derive(PartialEq, Eq)]`. +/// +/// In a more ideal world, we could check that requirement by just checking that +/// the given type implements both (1.) the `StructuralPartialEq` trait *and* +/// (2.) the `Eq` trait. However, you can have ADTs that *do* `derive(PartialEq, Eq)`, +/// and be a case that we want the compiler to accept, and yet the constant's +/// type fails to implement `Eq`. +/// +/// Namely, a case like this: +/// +/// ```rust +/// #[derive(PartialEq, Eq)] +/// struct Wrap<X>(X); +/// fn higher_order(_: &()) { } +/// const CFN: Wrap<fn(&())> = Wrap(higher_order); +/// fn main() { +/// match CFN { +/// CFN => {} +/// _ => {} +/// } +/// } +/// ``` +/// +/// (The problem in the above code is that `Wrap<fn(&())>` does not implement +/// `PartialEq`, nor `Eq`, because `for<'a> fn(&'a _)` does not implement those +/// traits.) +/// +/// Therefore, we cannot rely on naive check for `StructuralPartialEq` and +/// mere `Eq`. +/// +/// As a hack to work around this, we use two separate traits injected by each +/// of the two derives (`#[derive(PartialEq)]` and `#[derive(Eq)]`) and check +/// that both of them are present as part of structural-match checking. +#[cfg(not(bootstrap))] +#[unstable(feature = "structural_match", issue = "31434")] +#[rustc_on_unimplemented(message="the type `{Self}` does not `#[derive(Eq)]`")] +#[lang = "structural_teq"] +pub trait StructuralEq { + // Empty. +} + /// Types whose values can be duplicated simply by copying bits. /// /// By default, variable bindings have 'move semantics.' In other @@ -290,7 +369,6 @@ /// Derive macro generating an impl of the trait `Copy`. #[rustc_builtin_macro] -#[cfg_attr(bootstrap, rustc_macro_transparency = "semitransparent")] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow_internal_unstable(core_intrinsics, derive_clone_copy)] pub macro Copy($item:item) { /* compiler built-in */ } @@ -438,6 +516,14 @@ $t } } + + #[cfg(not(bootstrap))] + #[unstable(feature = "structural_match", issue = "31434")] + impl<T: ?Sized> StructuralPartialEq for $t<T> { } + + #[cfg(not(bootstrap))] + #[unstable(feature = "structural_match", issue = "31434")] + impl<T: ?Sized> StructuralEq for $t<T> { } ) }
diff --git a/src/libcore/mem/maybe_uninit.rs b/src/libcore/mem/maybe_uninit.rs index 9e9e901..792ce9d 100644 --- a/src/libcore/mem/maybe_uninit.rs +++ b/src/libcore/mem/maybe_uninit.rs
@@ -5,12 +5,12 @@ /// /// # Initialization invariant /// -/// The compiler, in general, assumes that variables are properly initialized -/// at their respective type. For example, a variable of reference type must -/// be aligned and non-NULL. This is an invariant that must *always* be upheld, -/// even in unsafe code. As a consequence, zero-initializing a variable of reference -/// type causes instantaneous [undefined behavior][ub], no matter whether that reference -/// ever gets used to access memory: +/// The compiler, in general, assumes that a variable is properly initialized +/// according to the requirements of the variable's type. For example, a variable of +/// reference type must be aligned and non-NULL. This is an invariant that must +/// *always* be upheld, even in unsafe code. As a consequence, zero-initializing a +/// variable of reference type causes instantaneous [undefined behavior][ub], +/// no matter whether that reference ever gets used to access memory: /// /// ```rust,no_run /// # #![allow(invalid_value)]
diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index 87ec05a..c7da56a 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs
@@ -236,7 +236,7 @@ /// ``` /// /// [alignment]: ./fn.align_of.html -#[inline] +#[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] pub const fn size_of<T>() -> usize { @@ -328,7 +328,7 @@ /// /// assert_eq!(4, mem::align_of::<i32>()); /// ``` -#[inline] +#[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] pub const fn align_of<T>() -> usize { @@ -368,15 +368,17 @@ /// make a difference in release builds (where a loop that has no side-effects /// is easily detected and eliminated), but is often a big win for debug builds. /// -/// Note that `ptr::drop_in_place` already performs this check, so if your workload -/// can be reduced to some small number of drop_in_place calls, using this is -/// unnecessary. In particular note that you can drop_in_place a slice, and that +/// Note that [`drop_in_place`] already performs this check, so if your workload +/// can be reduced to some small number of [`drop_in_place`] calls, using this is +/// unnecessary. In particular note that you can [`drop_in_place`] a slice, and that /// will do a single needs_drop check for all the values. /// /// Types like Vec therefore just `drop_in_place(&mut self[..])` without using -/// needs_drop explicitly. Types like `HashMap`, on the other hand, have to drop +/// `needs_drop` explicitly. Types like [`HashMap`], on the other hand, have to drop /// values one at a time and should use this API. /// +/// [`drop_in_place`]: ../ptr/fn.drop_in_place.html +/// [`HashMap`]: ../../std/collections/struct.HashMap.html /// /// # Examples /// @@ -518,8 +520,6 @@ /// A simple example: /// /// ``` -/// #![feature(mem_take)] -/// /// use std::mem; /// /// let mut v: Vec<i32> = vec![1, 2]; @@ -550,8 +550,6 @@ /// `self`, allowing it to be returned: /// /// ``` -/// #![feature(mem_take)] -/// /// use std::mem; /// /// # struct Buffer<T> { buf: Vec<T> } @@ -570,7 +568,7 @@ /// /// [`Clone`]: ../../std/clone/trait.Clone.html #[inline] -#[unstable(feature = "mem_take", issue = "61129")] +#[stable(feature = "mem_take", since = "1.40.0")] pub fn take<T: Default>(dest: &mut T) -> T { replace(dest, T::default()) } @@ -818,9 +816,9 @@ /// /// enum Foo { A(&'static str), B(i32), C(i32) } /// -/// assert!(mem::discriminant(&Foo::A("bar")) == mem::discriminant(&Foo::A("baz"))); -/// assert!(mem::discriminant(&Foo::B(1)) == mem::discriminant(&Foo::B(2))); -/// assert!(mem::discriminant(&Foo::B(3)) != mem::discriminant(&Foo::C(3))); +/// assert_eq!(mem::discriminant(&Foo::A("bar")), mem::discriminant(&Foo::A("baz"))); +/// assert_eq!(mem::discriminant(&Foo::B(1)), mem::discriminant(&Foo::B(2))); +/// assert_ne!(mem::discriminant(&Foo::B(3)), mem::discriminant(&Foo::C(3))); /// ``` #[stable(feature = "discriminant_value", since = "1.21.0")] pub fn discriminant<T>(v: &T) -> Discriminant<T> {
diff --git a/src/libcore/num/dec2flt/algorithm.rs b/src/libcore/num/dec2flt/algorithm.rs index fa3c807..ed89852 100644 --- a/src/libcore/num/dec2flt/algorithm.rs +++ b/src/libcore/num/dec2flt/algorithm.rs
@@ -143,13 +143,12 @@ /// > not a bound for the true error, but bounds the difference between the approximation z and /// > the best possible approximation that uses p bits of significand.) pub fn bellerophon<T: RawFloat>(f: &Big, e: i16) -> T { - let slop; - if f <= &Big::from_u64(T::MAX_SIG) { + let slop = if f <= &Big::from_u64(T::MAX_SIG) { // The cases abs(e) < log5(2^N) are in fast_path() - slop = if e >= 0 { 0 } else { 3 }; + if e >= 0 { 0 } else { 3 } } else { - slop = if e >= 0 { 1 } else { 4 }; - } + if e >= 0 { 1 } else { 4 } + }; let z = rawfp::big_to_fp(f).mul(&power_of_ten(e)).normalize(); let exp_p_n = 1 << (P - T::SIG_BITS as u32); let lowbits: i64 = (z.f % exp_p_n) as i64;
diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 22e7573..5730088 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs
@@ -466,11 +466,10 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let bytes = 12.5f32.to_be_bytes(); /// assert_eq!(bytes, [0x41, 0x48, 0x00, 0x00]); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn to_be_bytes(self) -> [u8; 4] { self.to_bits().to_be_bytes() @@ -482,11 +481,10 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let bytes = 12.5f32.to_le_bytes(); /// assert_eq!(bytes, [0x00, 0x00, 0x48, 0x41]); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn to_le_bytes(self) -> [u8; 4] { self.to_bits().to_le_bytes() @@ -504,7 +502,6 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let bytes = 12.5f32.to_ne_bytes(); /// assert_eq!( /// bytes, @@ -515,7 +512,7 @@ /// } /// ); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn to_ne_bytes(self) -> [u8; 4] { self.to_bits().to_ne_bytes() @@ -526,11 +523,10 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let value = f32::from_be_bytes([0x41, 0x48, 0x00, 0x00]); /// assert_eq!(value, 12.5); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn from_be_bytes(bytes: [u8; 4]) -> Self { Self::from_bits(u32::from_be_bytes(bytes)) @@ -541,11 +537,10 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let value = f32::from_le_bytes([0x00, 0x00, 0x48, 0x41]); /// assert_eq!(value, 12.5); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn from_le_bytes(bytes: [u8; 4]) -> Self { Self::from_bits(u32::from_le_bytes(bytes)) @@ -563,7 +558,6 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let value = f32::from_ne_bytes(if cfg!(target_endian = "big") { /// [0x41, 0x48, 0x00, 0x00] /// } else { @@ -571,7 +565,7 @@ /// }); /// assert_eq!(value, 12.5); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn from_ne_bytes(bytes: [u8; 4]) -> Self { Self::from_bits(u32::from_ne_bytes(bytes))
diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index bbe1d04..2bdeda3 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs
@@ -479,11 +479,10 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let bytes = 12.5f64.to_be_bytes(); /// assert_eq!(bytes, [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn to_be_bytes(self) -> [u8; 8] { self.to_bits().to_be_bytes() @@ -495,11 +494,10 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let bytes = 12.5f64.to_le_bytes(); /// assert_eq!(bytes, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40]); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn to_le_bytes(self) -> [u8; 8] { self.to_bits().to_le_bytes() @@ -517,7 +515,6 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let bytes = 12.5f64.to_ne_bytes(); /// assert_eq!( /// bytes, @@ -528,7 +525,7 @@ /// } /// ); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn to_ne_bytes(self) -> [u8; 8] { self.to_bits().to_ne_bytes() @@ -539,11 +536,10 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let value = f64::from_be_bytes([0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); /// assert_eq!(value, 12.5); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn from_be_bytes(bytes: [u8; 8]) -> Self { Self::from_bits(u64::from_be_bytes(bytes)) @@ -554,11 +550,10 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let value = f64::from_le_bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40]); /// assert_eq!(value, 12.5); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn from_le_bytes(bytes: [u8; 8]) -> Self { Self::from_bits(u64::from_le_bytes(bytes)) @@ -576,7 +571,6 @@ /// # Examples /// /// ``` - /// #![feature(float_to_from_bytes)] /// let value = f64::from_ne_bytes(if cfg!(target_endian = "big") { /// [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] /// } else { @@ -584,7 +578,7 @@ /// }); /// assert_eq!(value, 12.5); /// ``` - #[unstable(feature = "float_to_from_bytes", issue = "60446")] + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] #[inline] pub fn from_ne_bytes(bytes: [u8; 8]) -> Self { Self::from_bits(u64::from_ne_bytes(bytes))
diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 5d99c10..b4ade70 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs
@@ -252,7 +252,7 @@ $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[inline] + #[inline(always)] #[rustc_promotable] pub const fn min_value() -> Self { !0 ^ ((!0 as $UnsignedT) >> 1) as Self @@ -271,7 +271,7 @@ $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[inline] + #[inline(always)] #[rustc_promotable] pub const fn max_value() -> Self { !Self::min_value() @@ -938,7 +938,9 @@ ``` ", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); assert_eq!(", stringify!($SelfT), "::max_value().saturating_add(100), ", stringify!($SelfT), -"::max_value());", +"::max_value()); +assert_eq!(", stringify!($SelfT), "::min_value().saturating_add(-1), ", stringify!($SelfT), +"::min_value());", $EndFeature, " ```"), @@ -952,7 +954,6 @@ } } - doc_comment! { concat!("Saturating integer subtraction. Computes `self - rhs`, saturating at the numeric bounds instead of overflowing. @@ -964,7 +965,9 @@ ``` ", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27); assert_eq!(", stringify!($SelfT), "::min_value().saturating_sub(100), ", stringify!($SelfT), -"::min_value());", +"::min_value()); +assert_eq!(", stringify!($SelfT), "::max_value().saturating_sub(-1), ", stringify!($SelfT), +"::max_value());", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] @@ -1055,7 +1058,7 @@ #[inline] pub fn saturating_mul(self, rhs: Self) -> Self { self.checked_mul(rhs).unwrap_or_else(|| { - if (self < 0 && rhs < 0) || (self > 0 && rhs > 0) { + if (self < 0) == (rhs < 0) { Self::max_value() } else { Self::min_value() @@ -1112,13 +1115,7 @@ without modifying the original"] #[inline] pub const fn wrapping_add(self, rhs: Self) -> Self { - #[cfg(bootstrap)] { - intrinsics::overflowing_add(self, rhs) - } - - #[cfg(not(bootstrap))] { - intrinsics::wrapping_add(self, rhs) - } + intrinsics::wrapping_add(self, rhs) } } @@ -1141,13 +1138,7 @@ without modifying the original"] #[inline] pub const fn wrapping_sub(self, rhs: Self) -> Self { - #[cfg(bootstrap)] { - intrinsics::overflowing_sub(self, rhs) - } - - #[cfg(not(bootstrap))] { - intrinsics::wrapping_sub(self, rhs) - } + intrinsics::wrapping_sub(self, rhs) } } @@ -1169,13 +1160,7 @@ without modifying the original"] #[inline] pub const fn wrapping_mul(self, rhs: Self) -> Self { - #[cfg(bootstrap)] { - intrinsics::overflowing_mul(self, rhs) - } - - #[cfg(not(bootstrap))] { - intrinsics::wrapping_mul(self, rhs) - } + intrinsics::wrapping_mul(self, rhs) } } @@ -1402,7 +1387,16 @@ #[stable(feature = "no_panic_abs", since = "1.13.0")] #[inline] pub const fn wrapping_abs(self) -> Self { - (self ^ (self >> ($BITS - 1))).wrapping_sub(self >> ($BITS - 1)) + // sign is -1 (all ones) for negative numbers, 0 otherwise. + let sign = self >> ($BITS - 1); + // For positive self, sign == 0 so the expression is simply + // (self ^ 0).wrapping_sub(0) == self == abs(self). + // + // For negative self, self ^ sign == self ^ all_ones. + // But all_ones ^ self == all_ones - self == -1 - self. + // So for negative numbers, (self ^ sign).wrapping_sub(sign) is + // (-1 - self).wrapping_sub(-1) == -self == abs(self). + (self ^ sign).wrapping_sub(sign) } } @@ -1761,7 +1755,7 @@ #[stable(feature = "no_panic_abs", since = "1.13.0")] #[inline] pub const fn overflowing_abs(self) -> (Self, bool) { - (self ^ (self >> ($BITS - 1))).overflowing_sub(self >> ($BITS - 1)) + (self.wrapping_abs(), self == Self::min_value()) } } @@ -1870,7 +1864,7 @@ # Panics -This function will panic if `rhs` is 0. +This function will panic if `rhs` is 0 or the division results in overflow. # Examples @@ -1909,7 +1903,7 @@ # Panics -This function will panic if `rhs` is 0. +This function will panic if `rhs` is 0 or the division results in overflow. # Examples @@ -1969,7 +1963,21 @@ // Note that the #[inline] above means that the overflow // semantics of the subtraction depend on the crate we're being // inlined into. - (self ^ (self >> ($BITS - 1))) - (self >> ($BITS - 1)) + + // sign is -1 (all ones) for negative numbers, 0 otherwise. + let sign = self >> ($BITS - 1); + // For positive self, sign == 0 so the expression is simply + // (self ^ 0) - 0 == self == abs(self). + // + // For negative self, self ^ sign == self ^ all_ones. + // But all_ones ^ self == all_ones - self == -1 - self. + // So for negative numbers, (self ^ sign) - sign is + // (-1 - self) - -1 == -self == abs(self). + // + // The subtraction overflows when self is min_value(), because + // (-1 - min_value()) - -1 is max_value() - -1 which overflows. + // This is exactly when we want self.abs() to overflow. + (self ^ sign) - sign } } @@ -2303,7 +2311,7 @@ ```"), #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] - #[inline] + #[inline(always)] pub const fn min_value() -> Self { 0 } } @@ -2320,7 +2328,7 @@ ```"), #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] - #[inline] + #[inline(always)] pub const fn max_value() -> Self { !0 } } @@ -3040,13 +3048,7 @@ without modifying the original"] #[inline] pub const fn wrapping_add(self, rhs: Self) -> Self { - #[cfg(bootstrap)] { - intrinsics::overflowing_add(self, rhs) - } - - #[cfg(not(bootstrap))] { - intrinsics::wrapping_add(self, rhs) - } + intrinsics::wrapping_add(self, rhs) } } @@ -3068,13 +3070,7 @@ without modifying the original"] #[inline] pub const fn wrapping_sub(self, rhs: Self) -> Self { - #[cfg(bootstrap)] { - intrinsics::overflowing_sub(self, rhs) - } - - #[cfg(not(bootstrap))] { - intrinsics::wrapping_sub(self, rhs) - } + intrinsics::wrapping_sub(self, rhs) } } @@ -3097,13 +3093,7 @@ without modifying the original"] #[inline] pub const fn wrapping_mul(self, rhs: Self) -> Self { - #[cfg(bootstrap)] { - intrinsics::overflowing_mul(self, rhs) - } - - #[cfg(not(bootstrap))] { - intrinsics::wrapping_mul(self, rhs) - } + intrinsics::wrapping_mul(self, rhs) } doc_comment! { @@ -3704,6 +3694,10 @@ definitions of division are equal, this is exactly equal to `self / rhs`. +# Panics + +This function will panic if `rhs` is 0. + # Examples Basic usage: @@ -3729,6 +3723,10 @@ definitions of division are equal, this is exactly equal to `self % rhs`. +# Panics + +This function will panic if `rhs` is 0. + # Examples Basic usage: @@ -3759,8 +3757,8 @@ ```"), #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn is_power_of_two(self) -> bool { - (self.wrapping_sub(1)) & self == 0 && !(self == 0) + pub const fn is_power_of_two(self) -> bool { + self.count_ones() == 1 } }
diff --git a/src/libcore/num/wrapping.rs b/src/libcore/num/wrapping.rs index 59a10ae..5fe9895 100644 --- a/src/libcore/num/wrapping.rs +++ b/src/libcore/num/wrapping.rs
@@ -437,7 +437,7 @@ /// wrapping the truncated bits to the end of the resulting /// integer. /// - /// Please note this isn't the same operation as the `>>` shifting + /// Please note this isn't the same operation as the `<<` shifting /// operator! /// /// # Examples @@ -463,7 +463,7 @@ /// wrapping the truncated bits to the beginning of the resulting /// integer. /// - /// Please note this isn't the same operation as the `<<` shifting + /// Please note this isn't the same operation as the `>>` shifting /// operator! /// /// # Examples
diff --git a/src/libcore/ops/try.rs b/src/libcore/ops/try.rs index 76fec10..e8f35f8 100644 --- a/src/libcore/ops/try.rs +++ b/src/libcore/ops/try.rs
@@ -5,7 +5,7 @@ /// extracting those success or failure values from an existing instance and /// creating a new instance from a success or failure value. #[unstable(feature = "try_trait", issue = "42327")] -#[rustc_on_unimplemented( +#[cfg_attr(bootstrap, rustc_on_unimplemented( on(all( any(from_method="from_error", from_method="from_ok"), from_desugaring="QuestionMark"), @@ -17,7 +17,20 @@ message="the `?` operator can only be applied to values \ that implement `{Try}`", label="the `?` operator cannot be applied to type `{Self}`") -)] +))] +#[cfg_attr(not(bootstrap), rustc_on_unimplemented( +on(all( +any(from_method="from_error", from_method="from_ok"), +from_desugaring="QuestionMark"), +message="the `?` operator can only be used in {ItemContext} \ + that returns `Result` or `Option` \ + (or another type that implements `{Try}`)", +label="cannot use the `?` operator in {ItemContext} that returns `{Self}`"), +on(all(from_method="into_result", from_desugaring="QuestionMark"), +message="the `?` operator can only be applied to values \ + that implement `{Try}`", +label="the `?` operator cannot be applied to type `{Self}`") +))] #[doc(alias = "?")] pub trait Try { /// The type of this value when viewed as successful.
diff --git a/src/libcore/ops/unsize.rs b/src/libcore/ops/unsize.rs index 8e46830..d291476 100644 --- a/src/libcore/ops/unsize.rs +++ b/src/libcore/ops/unsize.rs
@@ -76,7 +76,7 @@ /// ``` /// # #![feature(dispatch_from_dyn, unsize)] /// # use std::{ops::DispatchFromDyn, marker::Unsize}; -/// # struct Rc<T: ?Sized>(::std::rc::Rc<T>); +/// # struct Rc<T: ?Sized>(std::rc::Rc<T>); /// impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Rc<U>> for Rc<T> /// where /// T: Unsize<U>,
diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 5569d99..f0ac5e7 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs
@@ -46,7 +46,7 @@ //! # Options and pointers ("nullable" pointers) //! //! Rust's pointer types must always point to a valid location; there are -//! no "null" pointers. Instead, Rust has *optional* pointers, like +//! no "null" references. Instead, Rust has *optional* pointers, like //! the optional owned box, [`Option`]`<`[`Box<T>`]`>`. //! //! The following example uses [`Option`] to create an optional box of @@ -64,7 +64,7 @@ //! //! fn check_optional(optional: Option<Box<i32>>) { //! match optional { -//! Some(ref p) => println!("has value {}", p), +//! Some(p) => println!("has value {}", p), //! None => println!("has no value"), //! } //! } @@ -83,7 +83,7 @@ //! let msg = Some("howdy"); //! //! // Take a reference to the contained string -//! if let Some(ref m) = msg { +//! if let Some(m) = &msg { //! println!("{}", *m); //! } //! @@ -395,10 +395,10 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap_or(self, def: T) -> T { + pub fn unwrap_or(self, default: T) -> T { match self { Some(x) => x, - None => def, + None => default, } } @@ -837,9 +837,8 @@ #[inline] #[stable(feature = "option_entry", since = "1.20.0")] pub fn get_or_insert_with<F: FnOnce() -> T>(&mut self, f: F) -> &mut T { - match *self { - None => *self = Some(f()), - _ => (), + if let None = *self { + *self = Some(f()); } match *self { @@ -1102,7 +1101,6 @@ } } -#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")] impl<T: Deref> Option<T> { /// Converts from `Option<T>` (or `&Option<T>`) to `Option<&T::Target>`. /// @@ -1114,20 +1112,18 @@ /// # Examples /// /// ``` - /// #![feature(inner_deref)] - /// /// let x: Option<String> = Some("hey".to_owned()); /// assert_eq!(x.as_deref(), Some("hey")); /// /// let x: Option<String> = None; /// assert_eq!(x.as_deref(), None); /// ``` + #[stable(feature = "option_deref", since = "1.40.0")] pub fn as_deref(&self) -> Option<&T::Target> { self.as_ref().map(|t| t.deref()) } } -#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")] impl<T: DerefMut> Option<T> { /// Converts from `Option<T>` (or `&mut Option<T>`) to `Option<&mut T::Target>`. /// @@ -1137,14 +1133,13 @@ /// # Examples /// /// ``` - /// #![feature(inner_deref)] - /// /// let mut x: Option<String> = Some("hey".to_owned()); /// assert_eq!(x.as_deref_mut().map(|x| { /// x.make_ascii_uppercase(); /// x /// }), Some("HEY".to_owned().as_mut_str())); /// ``` + #[stable(feature = "option_deref", since = "1.40.0")] pub fn as_deref_mut(&mut self) -> Option<&mut T::Target> { self.as_mut().map(|t| t.deref_mut()) } @@ -1572,7 +1567,6 @@ /// # Examples /// Basic usage: /// ``` - /// #![feature(option_flattening)] /// let x: Option<Option<u32>> = Some(Some(6)); /// assert_eq!(Some(6), x.flatten()); /// @@ -1584,13 +1578,12 @@ /// ``` /// Flattening once only removes one level of nesting: /// ``` - /// #![feature(option_flattening)] /// let x: Option<Option<Option<u32>>> = Some(Some(Some(6))); /// assert_eq!(Some(Some(6)), x.flatten()); /// assert_eq!(Some(6), x.flatten().flatten()); /// ``` #[inline] - #[unstable(feature = "option_flattening", issue = "60258")] + #[stable(feature = "option_flattening", since = "1.40.0")] pub fn flatten(self) -> Option<T> { self.and_then(convert::identity) }
diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs index 989fc96..51bbf3a 100644 --- a/src/libcore/panic.rs +++ b/src/libcore/panic.rs
@@ -35,7 +35,7 @@ pub struct PanicInfo<'a> { payload: &'a (dyn Any + Send), message: Option<&'a fmt::Arguments<'a>>, - location: Location<'a>, + location: &'a Location<'a>, } impl<'a> PanicInfo<'a> { @@ -45,11 +45,16 @@ issue = "0")] #[doc(hidden)] #[inline] - pub fn internal_constructor(message: Option<&'a fmt::Arguments<'a>>, - location: Location<'a>) - -> Self { + pub fn internal_constructor( + message: Option<&'a fmt::Arguments<'a>>, + location: &'a Location<'a>, + ) -> Self { struct NoPayload; - PanicInfo { payload: &NoPayload, location, message } + PanicInfo { + location, + message, + payload: &NoPayload, + } } #[doc(hidden)] @@ -162,6 +167,7 @@ /// /// panic!("Normal panic"); /// ``` +#[cfg_attr(not(bootstrap), lang = "panic_location")] #[derive(Debug)] #[stable(feature = "panic_hooks", since = "1.10.0")] pub struct Location<'a> { @@ -176,7 +182,7 @@ and related macros", issue = "0")] #[doc(hidden)] - pub fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self { + pub const fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self { Location { file, line, col } }
diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 7b72534..685b749 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs
@@ -29,6 +29,7 @@ use crate::fmt; use crate::panic::{Location, PanicInfo}; +#[cfg(bootstrap)] #[cold] // never inline unless panic_immediate_abort to avoid code // bloat at the call sites as much as possible @@ -49,6 +50,27 @@ panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), &(file, line, col)) } +#[cfg(not(bootstrap))] +#[cold] +// never inline unless panic_immediate_abort to avoid code +// bloat at the call sites as much as possible +#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] +#[lang = "panic"] +pub fn panic(expr: &str, location: &Location<'_>) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { super::intrinsics::abort() } + } + + // Use Arguments::new_v1 instead of format_args!("{}", expr) to potentially + // reduce size overhead. The format_args! macro uses str's Display trait to + // write expr, which calls Formatter::pad, which must accommodate string + // truncation and padding (even though none is used here). Using + // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the + // output binary, saving up to a few kilobytes. + panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), location) +} + +#[cfg(bootstrap)] #[cold] #[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] #[lang = "panic_bounds_check"] @@ -62,6 +84,22 @@ len, index), file_line_col) } +#[cfg(not(bootstrap))] +#[cold] +#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] +#[lang = "panic_bounds_check"] +fn panic_bounds_check(location: &Location<'_>, index: usize, len: usize) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { super::intrinsics::abort() } + } + + panic_fmt( + format_args!("index out of bounds: the len is {} but the index is {}", len, index), + location + ) +} + +#[cfg(bootstrap)] #[cold] #[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] #[cfg_attr( feature="panic_immediate_abort" ,inline)] @@ -71,16 +109,32 @@ } // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call - #[cfg_attr(bootstrap, allow(improper_ctypes))] extern "Rust" { #[lang = "panic_impl"] fn panic_impl(pi: &PanicInfo<'_>) -> !; } let (file, line, col) = *file_line_col; - let pi = PanicInfo::internal_constructor( - Some(&fmt), - Location::internal_constructor(file, line, col), - ); + let location = Location::internal_constructor(file, line, col); + let pi = PanicInfo::internal_constructor(Some(&fmt), &location); + unsafe { panic_impl(&pi) } +} + +#[cfg(not(bootstrap))] +#[cold] +#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] +#[cfg_attr( feature="panic_immediate_abort" ,inline)] +pub fn panic_fmt(fmt: fmt::Arguments<'_>, location: &Location<'_>) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { super::intrinsics::abort() } + } + + // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call + extern "Rust" { + #[lang = "panic_impl"] + fn panic_impl(pi: &PanicInfo<'_>) -> !; + } + + let pi = PanicInfo::internal_constructor(Some(&fmt), location); unsafe { panic_impl(&pi) } }
diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index 1dc6d54..be057ed 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs
@@ -369,6 +369,8 @@ //! [drop-guarantee]: #drop-guarantee //! [`poll`]: ../../std/future/trait.Future.html#tymethod.poll //! [`Pin::get_unchecked_mut`]: struct.Pin.html#method.get_unchecked_mut +//! [`bool`]: ../../std/primitive.bool.html +//! [`i32`]: ../../std/primitive.i32.html #![stable(feature = "pin", since = "1.33.0")]
diff --git a/src/libcore/ptr/mod.rs b/src/libcore/ptr/mod.rs index 13ccc9b..1355ce1 100644 --- a/src/libcore/ptr/mod.rs +++ b/src/libcore/ptr/mod.rs
@@ -188,7 +188,7 @@ /// let p: *const i32 = ptr::null(); /// assert!(p.is_null()); /// ``` -#[inline] +#[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] pub const fn null<T>() -> *const T { 0 as *const T } @@ -203,7 +203,7 @@ /// let p: *mut i32 = ptr::null_mut(); /// assert!(p.is_null()); /// ``` -#[inline] +#[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] pub const fn null_mut<T>() -> *mut T { 0 as *mut T } @@ -1286,7 +1286,22 @@ /// } /// ``` #[unstable(feature = "ptr_offset_from", issue = "41079")] + #[cfg(not(bootstrap))] + #[rustc_const_unstable(feature = "const_ptr_offset_from")] #[inline] + pub const unsafe fn offset_from(self, origin: *const T) -> isize where T: Sized { + let pointee_size = mem::size_of::<T>(); + let ok = 0 < pointee_size && pointee_size <= isize::max_value() as usize; + // assert that the pointee size is valid in a const eval compatible way + // FIXME: do this with a real assert at some point + [()][(!ok) as usize]; + intrinsics::ptr_offset_from(self, origin) + } + + #[unstable(feature = "ptr_offset_from", issue = "41079")] + #[inline] + #[cfg(bootstrap)] + /// bootstrap pub unsafe fn offset_from(self, origin: *const T) -> isize where T: Sized { let pointee_size = mem::size_of::<T>(); assert!(0 < pointee_size && pointee_size <= isize::max_value() as usize); @@ -2013,8 +2028,9 @@ /// } /// ``` #[unstable(feature = "ptr_offset_from", issue = "41079")] + #[rustc_const_unstable(feature = "const_ptr_offset_from")] #[inline] - pub unsafe fn offset_from(self, origin: *const T) -> isize where T: Sized { + pub const unsafe fn offset_from(self, origin: *const T) -> isize where T: Sized { (self as *const T).offset_from(origin) } @@ -2732,31 +2748,29 @@ /// impl Trait for Wrapper {} /// impl Trait for i32 {} /// -/// fn main() { -/// let wrapper = Wrapper { member: 10 }; +/// let wrapper = Wrapper { member: 10 }; /// -/// // Pointers have equal addresses. -/// assert!(std::ptr::eq( -/// &wrapper as *const Wrapper as *const u8, -/// &wrapper.member as *const i32 as *const u8 -/// )); +/// // Pointers have equal addresses. +/// assert!(std::ptr::eq( +/// &wrapper as *const Wrapper as *const u8, +/// &wrapper.member as *const i32 as *const u8 +/// )); /// -/// // Objects have equal addresses, but `Trait` has different implementations. -/// assert!(!std::ptr::eq( -/// &wrapper as &dyn Trait, -/// &wrapper.member as &dyn Trait, -/// )); -/// assert!(!std::ptr::eq( -/// &wrapper as &dyn Trait as *const dyn Trait, -/// &wrapper.member as &dyn Trait as *const dyn Trait, -/// )); +/// // Objects have equal addresses, but `Trait` has different implementations. +/// assert!(!std::ptr::eq( +/// &wrapper as &dyn Trait, +/// &wrapper.member as &dyn Trait, +/// )); +/// assert!(!std::ptr::eq( +/// &wrapper as &dyn Trait as *const dyn Trait, +/// &wrapper.member as &dyn Trait as *const dyn Trait, +/// )); /// -/// // Converting the reference to a `*const u8` compares by address. -/// assert!(std::ptr::eq( -/// &wrapper as &dyn Trait as *const dyn Trait as *const u8, -/// &wrapper.member as &dyn Trait as *const dyn Trait as *const u8, -/// )); -/// } +/// // Converting the reference to a `*const u8` compares by address. +/// assert!(std::ptr::eq( +/// &wrapper as &dyn Trait as *const dyn Trait as *const u8, +/// &wrapper.member as &dyn Trait as *const dyn Trait as *const u8, +/// )); /// ``` #[stable(feature = "ptr_eq", since = "1.17.0")] #[inline]
diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 0c2a4e0..cdada12 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs
@@ -28,7 +28,7 @@ use crate::intrinsics::{assume, exact_div, unchecked_sub, is_aligned_and_not_null}; use crate::isize; use crate::iter::*; -use crate::ops::{FnMut, Try, self}; +use crate::ops::{FnMut, Range, self}; use crate::option::Option; use crate::option::Option::{None, Some}; use crate::result::Result; @@ -62,9 +62,9 @@ /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_slice_len"))] // SAFETY: const sound because we transmute out the length field as a usize (which it must be) - #[cfg_attr(not(bootstrap), allow_internal_unstable(const_fn_union))] + #[allow(unused_attributes)] + #[allow_internal_unstable(const_fn_union)] pub const fn len(&self) -> usize { unsafe { crate::ptr::Repr { rust: self }.raw.len @@ -81,7 +81,6 @@ /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_slice_len"))] pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -408,6 +407,86 @@ self as *mut [T] as *mut T } + /// Returns the two raw pointers spanning the slice. + /// + /// The returned range is half-open, which means that the end pointer + /// points *one past* the last element of the slice. This way, an empty + /// slice is represented by two equal pointers, and the difference between + /// the two pointers represents the size of the size. + /// + /// See [`as_ptr`] for warnings on using these pointers. The end pointer + /// requires extra caution, as it does not point to a valid element in the + /// slice. + /// + /// This function is useful for interacting with foreign interfaces which + /// use two pointers to refer to a range of elements in memory, as is + /// common in C++. + /// + /// It can also be useful to check if a pointer to an element refers to an + /// element of this slice: + /// + /// ``` + /// #![feature(slice_ptr_range)] + /// + /// let a = [1, 2, 3]; + /// let x = &a[1] as *const _; + /// let y = &5 as *const _; + /// + /// assert!(a.as_ptr_range().contains(&x)); + /// assert!(!a.as_ptr_range().contains(&y)); + /// ``` + /// + /// [`as_ptr`]: #method.as_ptr + #[unstable(feature = "slice_ptr_range", issue = "65807")] + #[inline] + pub fn as_ptr_range(&self) -> Range<*const T> { + // The `add` here is safe, because: + // + // - Both pointers are part of the same object, as pointing directly + // past the object also counts. + // + // - The size of the slice is never larger than isize::MAX bytes, as + // noted here: + // - https://github.com/rust-lang/unsafe-code-guidelines/issues/102#issuecomment-473340447 + // - https://doc.rust-lang.org/reference/behavior-considered-undefined.html + // - https://doc.rust-lang.org/core/slice/fn.from_raw_parts.html#safety + // (This doesn't seem normative yet, but the very same assumption is + // made in many places, including the Index implementation of slices.) + // + // - There is no wrapping around involved, as slices do not wrap past + // the end of the address space. + // + // See the documentation of pointer::add. + let start = self.as_ptr(); + let end = unsafe { start.add(self.len()) }; + start..end + } + + /// Returns the two unsafe mutable pointers spanning the slice. + /// + /// The returned range is half-open, which means that the end pointer + /// points *one past* the last element of the slice. This way, an empty + /// slice is represented by two equal pointers, and the difference between + /// the two pointers represents the size of the size. + /// + /// See [`as_mut_ptr`] for warnings on using these pointers. The end + /// pointer requires extra caution, as it does not point to a valid element + /// in the slice. + /// + /// This function is useful for interacting with foreign interfaces which + /// use two pointers to refer to a range of elements in memory, as is + /// common in C++. + /// + /// [`as_mut_ptr`]: #method.as_mut_ptr + #[unstable(feature = "slice_ptr_range", issue = "65807")] + #[inline] + pub fn as_mut_ptr_range(&mut self) -> Range<*mut T> { + // See as_ptr_range() above for why `add` here is safe. + let start = self.as_mut_ptr(); + let end = unsafe { start.add(self.len()) }; + start..end + } + /// Swaps two elements in the slice. /// /// # Arguments @@ -3183,39 +3262,6 @@ } #[inline] - fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R where - Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B> - { - // manual unrolling is needed when there are conditional exits from the loop - let mut accum = init; - unsafe { - while len!(self) >= 4 { - accum = f(accum, next_unchecked!(self))?; - accum = f(accum, next_unchecked!(self))?; - accum = f(accum, next_unchecked!(self))?; - accum = f(accum, next_unchecked!(self))?; - } - while !is_empty!(self) { - accum = f(accum, next_unchecked!(self))?; - } - } - Try::from_ok(accum) - } - - #[inline] - fn fold<Acc, Fold>(mut self, init: Acc, mut f: Fold) -> Acc - where Fold: FnMut(Acc, Self::Item) -> Acc, - { - // Let LLVM unroll this, rather than using the default - // impl that would force the manual unrolling above - let mut accum = init; - while let Some(x) = self.next() { - accum = f(accum, x); - } - accum - } - - #[inline] #[rustc_inherit_overflow_checks] fn position<P>(&mut self, mut predicate: P) -> Option<usize> where Self: Sized, @@ -3285,40 +3331,6 @@ Some(next_back_unchecked!(self)) } } - - #[inline] - fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R where - Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B> - { - // manual unrolling is needed when there are conditional exits from the loop - let mut accum = init; - unsafe { - while len!(self) >= 4 { - accum = f(accum, next_back_unchecked!(self))?; - accum = f(accum, next_back_unchecked!(self))?; - accum = f(accum, next_back_unchecked!(self))?; - accum = f(accum, next_back_unchecked!(self))?; - } - // inlining is_empty everywhere makes a huge performance difference - while !is_empty!(self) { - accum = f(accum, next_back_unchecked!(self))?; - } - } - Try::from_ok(accum) - } - - #[inline] - fn rfold<Acc, Fold>(mut self, init: Acc, mut f: Fold) -> Acc - where Fold: FnMut(Acc, Self::Item) -> Acc, - { - // Let LLVM unroll this, rather than using the default - // impl that would force the manual unrolling above - let mut accum = init; - while let Some(x) = self.next_back() { - accum = f(accum, x); - } - accum - } } #[stable(feature = "fused", since = "1.26.0")]
diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index a6ec757..f67012d 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs
@@ -176,7 +176,7 @@ /// ``` /// fn from_utf8_lossy<F>(mut input: &[u8], mut push: F) where F: FnMut(&str) { /// loop { -/// match ::std::str::from_utf8(input) { +/// match std::str::from_utf8(input) { /// Ok(valid) => { /// push(valid); /// break @@ -184,7 +184,7 @@ /// Err(error) => { /// let (valid, after_valid) = input.split_at(error.valid_up_to()); /// unsafe { -/// push(::std::str::from_utf8_unchecked(valid)) +/// push(std::str::from_utf8_unchecked(valid)) /// } /// push("\u{FFFD}"); /// @@ -2090,7 +2090,6 @@ /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_str_len"))] pub const fn len(&self) -> usize { self.as_bytes().len() } @@ -2110,7 +2109,6 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_str_len"))] pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -2168,9 +2166,9 @@ /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline(always)] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_str_as_bytes"))] // SAFETY: const sound because we transmute two types with the same layout - #[cfg_attr(not(bootstrap), allow_internal_unstable(const_fn_union))] + #[allow(unused_attributes)] + #[allow_internal_unstable(const_fn_union)] pub const fn as_bytes(&self) -> &[u8] { #[repr(C)] union Slices<'a> {
diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index c9ccef9..73d5abf 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs
@@ -18,11 +18,11 @@ //! //! Each method takes an [`Ordering`] which represents the strength of //! the memory barrier for that operation. These orderings are the -//! same as [LLVM atomic orderings][1]. For more information see the [nomicon][2]. +//! same as the [C++20 atomic orderings][1]. For more information see the [nomicon][2]. //! //! [`Ordering`]: enum.Ordering.html //! -//! [1]: https://llvm.org/docs/LangRef.html#memory-model-for-concurrent-operations +//! [1]: https://en.cppreference.com/w/cpp/atomic/memory_order //! [2]: ../../../nomicon/atomics.html //! //! Atomic variables are safe to share between threads (they implement [`Sync`]) @@ -113,8 +113,8 @@ //! ``` #![stable(feature = "rust1", since = "1.0.0")] -#![cfg_attr(not(target_has_atomic = "8"), allow(dead_code))] -#![cfg_attr(not(target_has_atomic = "8"), allow(unused_imports))] +#![cfg_attr(not(target_has_atomic_load_store = "8"), allow(dead_code))] +#![cfg_attr(not(target_has_atomic_load_store = "8"), allow(unused_imports))] use self::Ordering::*; @@ -160,14 +160,14 @@ /// This type has the same in-memory representation as a [`bool`]. /// /// [`bool`]: ../../../std/primitive.bool.html -#[cfg(target_has_atomic = "8")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "8"))] #[stable(feature = "rust1", since = "1.0.0")] #[repr(C, align(1))] pub struct AtomicBool { v: UnsafeCell<u8>, } -#[cfg(target_has_atomic = "8")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "8"))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for AtomicBool { /// Creates an `AtomicBool` initialized to `false`. @@ -177,14 +177,14 @@ } // Send is implicitly implemented for AtomicBool. -#[cfg(target_has_atomic = "8")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "8"))] #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Sync for AtomicBool {} /// A raw pointer type which can be safely shared between threads. /// /// This type has the same in-memory representation as a `*mut T`. -#[cfg(target_has_atomic = "ptr")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(target_pointer_width = "16", repr(C, align(2)))] #[cfg_attr(target_pointer_width = "32", repr(C, align(4)))] @@ -193,7 +193,7 @@ p: UnsafeCell<*mut T>, } -#[cfg(target_has_atomic = "ptr")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] impl<T> Default for AtomicPtr<T> { /// Creates a null `AtomicPtr<T>`. @@ -202,10 +202,10 @@ } } -#[cfg(target_has_atomic = "ptr")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T> Send for AtomicPtr<T> {} -#[cfg(target_has_atomic = "ptr")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T> Sync for AtomicPtr<T> {} @@ -217,8 +217,8 @@ /// operations synchronize other memory while additionally preserving a total order of such /// operations across all threads. /// -/// Rust's memory orderings are [the same as -/// LLVM's](https://llvm.org/docs/LangRef.html#memory-model-for-concurrent-operations). +/// Rust's memory orderings are [the same as those of +/// C++20](https://en.cppreference.com/w/cpp/atomic/memory_order). /// /// For more information see the [nomicon]. /// @@ -231,9 +231,9 @@ pub enum Ordering { /// No ordering constraints, only atomic operations. /// - /// Corresponds to LLVM's [`Monotonic`] ordering. + /// Corresponds to [`memory_order_relaxed`] in C++20. /// - /// [`Monotonic`]: https://llvm.org/docs/Atomics.html#monotonic + /// [`memory_order_relaxed`]: https://en.cppreference.com/w/cpp/atomic/memory_order#Relaxed_ordering #[stable(feature = "rust1", since = "1.0.0")] Relaxed, /// When coupled with a store, all previous operations become ordered @@ -246,11 +246,12 @@ /// /// This ordering is only applicable for operations that can perform a store. /// - /// Corresponds to LLVM's [`Release`] ordering. + /// Corresponds to [`memory_order_release`] in C++20. /// - /// [`Release`]: https://llvm.org/docs/Atomics.html#release - /// [`Acquire`]: https://llvm.org/docs/Atomics.html#acquire - /// [`Relaxed`]: https://llvm.org/docs/Atomics.html#monotonic + /// [`Release`]: #variant.Release + /// [`Acquire`]: #variant.Acquire + /// [`Relaxed`]: #variant.Relaxed + /// [`memory_order_release`]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release-Acquire_ordering #[stable(feature = "rust1", since = "1.0.0")] Release, /// When coupled with a load, if the loaded value was written by a store operation with @@ -263,11 +264,12 @@ /// /// This ordering is only applicable for operations that can perform a load. /// - /// Corresponds to LLVM's [`Acquire`] ordering. + /// Corresponds to [`memory_order_acquire`] in C++20. /// - /// [`Acquire`]: https://llvm.org/docs/Atomics.html#acquire - /// [`Release`]: https://llvm.org/docs/Atomics.html#release - /// [`Relaxed`]: https://llvm.org/docs/Atomics.html#monotonic + /// [`Acquire`]: #variant.Acquire + /// [`Release`]: #variant.Release + /// [`Relaxed`]: #variant.Relaxed + /// [`memory_order_acquire`]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release-Acquire_ordering #[stable(feature = "rust1", since = "1.0.0")] Acquire, /// Has the effects of both [`Acquire`] and [`Release`] together: @@ -275,28 +277,28 @@ /// /// Notice that in the case of `compare_and_swap`, it is possible that the operation ends up /// not performing any store and hence it has just [`Acquire`] ordering. However, - /// [`AcqRel`][`AcquireRelease`] will never perform [`Relaxed`] accesses. + /// `AcqRel` will never perform [`Relaxed`] accesses. /// /// This ordering is only applicable for operations that combine both loads and stores. /// - /// Corresponds to LLVM's [`AcquireRelease`] ordering. + /// Corresponds to [`memory_order_acq_rel`] in C++20. /// - /// [`AcquireRelease`]: https://llvm.org/docs/Atomics.html#acquirerelease - /// [`Acquire`]: https://llvm.org/docs/Atomics.html#acquire - /// [`Release`]: https://llvm.org/docs/Atomics.html#release - /// [`Relaxed`]: https://llvm.org/docs/Atomics.html#monotonic + /// [`memory_order_acq_rel`]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release-Acquire_ordering + /// [`Acquire`]: #variant.Acquire + /// [`Release`]: #variant.Release + /// [`Relaxed`]: #variant.Relaxed #[stable(feature = "rust1", since = "1.0.0")] AcqRel, /// Like [`Acquire`]/[`Release`]/[`AcqRel`] (for load, store, and load-with-store /// operations, respectively) with the additional guarantee that all threads see all /// sequentially consistent operations in the same order. /// - /// Corresponds to LLVM's [`SequentiallyConsistent`] ordering. + /// Corresponds to [`memory_order_seq_cst`] in C++20. /// - /// [`SequentiallyConsistent`]: https://llvm.org/docs/Atomics.html#sequentiallyconsistent - /// [`Acquire`]: https://llvm.org/docs/Atomics.html#acquire - /// [`Release`]: https://llvm.org/docs/Atomics.html#release - /// [`AcqRel`]: https://llvm.org/docs/Atomics.html#acquirerelease + /// [`memory_order_seq_cst`]: https://en.cppreference.com/w/cpp/atomic/memory_order#Sequentially-consistent_ordering + /// [`Acquire`]: #variant.Acquire + /// [`Release`]: #variant.Release + /// [`AcqRel`]: #variant.AcqRel #[stable(feature = "rust1", since = "1.0.0")] SeqCst, } @@ -304,7 +306,7 @@ /// An [`AtomicBool`] initialized to `false`. /// /// [`AtomicBool`]: struct.AtomicBool.html -#[cfg(target_has_atomic = "8")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "8"))] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_deprecated( since = "1.34.0", @@ -313,7 +315,7 @@ )] pub const ATOMIC_BOOL_INIT: AtomicBool = AtomicBool::new(false); -#[cfg(target_has_atomic = "8")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "8"))] impl AtomicBool { /// Creates a new `AtomicBool`. /// @@ -462,7 +464,7 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "8")] pub fn swap(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_swap(self.v.get(), val as u8, order) != 0 } } @@ -500,7 +502,7 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "8")] pub fn compare_and_swap(&self, current: bool, new: bool, order: Ordering) -> bool { match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) { Ok(x) => x, @@ -551,7 +553,7 @@ /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "8")] pub fn compare_exchange(&self, current: bool, new: bool, @@ -607,7 +609,7 @@ /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "8")] pub fn compare_exchange_weak(&self, current: bool, new: bool, @@ -658,7 +660,7 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "8")] pub fn fetch_and(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_and(self.v.get(), val as u8, order) != 0 } } @@ -700,7 +702,7 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "8")] pub fn fetch_nand(&self, val: bool, order: Ordering) -> bool { // We can't use atomic_nand here because it can result in a bool with // an invalid value. This happens because the atomic operation is done @@ -753,7 +755,7 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "8")] pub fn fetch_or(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_or(self.v.get(), val as u8, order) != 0 } } @@ -794,13 +796,13 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "8")] pub fn fetch_xor(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_xor(self.v.get(), val as u8, order) != 0 } } } -#[cfg(target_has_atomic = "ptr")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] impl<T> AtomicPtr<T> { /// Creates a new `AtomicPtr`. /// @@ -951,7 +953,7 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "ptr")] pub fn swap(&self, ptr: *mut T, order: Ordering) -> *mut T { unsafe { atomic_swap(self.p.get() as *mut usize, ptr as usize, order) as *mut T } } @@ -987,7 +989,7 @@ /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "ptr")] pub fn compare_and_swap(&self, current: *mut T, new: *mut T, order: Ordering) -> *mut T { match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) { Ok(x) => x, @@ -1029,7 +1031,7 @@ /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "ptr")] pub fn compare_exchange(&self, current: *mut T, new: *mut T, @@ -1089,7 +1091,7 @@ /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] - #[cfg(target_has_atomic = "cas")] + #[cfg(target_has_atomic = "ptr")] pub fn compare_exchange_weak(&self, current: *mut T, new: *mut T, @@ -1110,7 +1112,7 @@ } } -#[cfg(target_has_atomic = "8")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "8"))] #[stable(feature = "atomic_bool_from", since = "1.24.0")] impl From<bool> for AtomicBool { /// Converts a `bool` into an `AtomicBool`. @@ -1126,16 +1128,17 @@ fn from(b: bool) -> Self { Self::new(b) } } -#[cfg(target_has_atomic = "ptr")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] #[stable(feature = "atomic_from", since = "1.23.0")] impl<T> From<*mut T> for AtomicPtr<T> { #[inline] fn from(p: *mut T) -> Self { Self::new(p) } } -#[cfg(target_has_atomic = "ptr")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "8"))] macro_rules! atomic_int { - ($stable:meta, + ($cfg_cas:meta, + $stable:meta, $stable_cxchg:meta, $stable_debug:meta, $stable_access:meta, @@ -1356,7 +1359,7 @@ ```"), #[inline] #[$stable] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_swap(self.v.get(), val, order) } } @@ -1396,7 +1399,7 @@ ```"), #[inline] #[$stable] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn compare_and_swap(&self, current: $int_type, new: $int_type, @@ -1454,7 +1457,7 @@ ```"), #[inline] #[$stable_cxchg] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn compare_exchange(&self, current: $int_type, new: $int_type, @@ -1506,7 +1509,7 @@ ```"), #[inline] #[$stable_cxchg] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn compare_exchange_weak(&self, current: $int_type, new: $int_type, @@ -1544,7 +1547,7 @@ ```"), #[inline] #[$stable] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_add(self.v.get(), val, order) } } @@ -1576,7 +1579,7 @@ ```"), #[inline] #[$stable] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_sub(self.v.get(), val, order) } } @@ -1611,7 +1614,7 @@ ```"), #[inline] #[$stable] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_and(self.v.get(), val, order) } } @@ -1647,7 +1650,7 @@ ```"), #[inline] #[$stable_nand] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_nand(self.v.get(), val, order) } } @@ -1682,7 +1685,7 @@ ```"), #[inline] #[$stable] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_or(self.v.get(), val, order) } } @@ -1717,7 +1720,7 @@ ```"), #[inline] #[$stable] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_xor(self.v.get(), val, order) } } @@ -1767,7 +1770,7 @@ #[unstable(feature = "no_more_cas", reason = "no more CAS loops in user code", issue = "48655")] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn fetch_update<F>(&self, mut f: F, fetch_order: Ordering, @@ -1828,7 +1831,7 @@ #[unstable(feature = "atomic_min_max", reason = "easier and faster min/max than writing manual CAS loop", issue = "48655")] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { $max_fn(self.v.get(), val, order) } } @@ -1880,7 +1883,7 @@ #[unstable(feature = "atomic_min_max", reason = "easier and faster min/max than writing manual CAS loop", issue = "48655")] - #[cfg(target_has_atomic = "cas")] + #[$cfg_cas] pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { $min_fn(self.v.get(), val, order) } } @@ -1890,8 +1893,9 @@ } } -#[cfg(target_has_atomic = "8")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "8"))] atomic_int! { + cfg(target_has_atomic = "8"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), @@ -1906,8 +1910,9 @@ "AtomicI8::new(0)", i8 AtomicI8 ATOMIC_I8_INIT } -#[cfg(target_has_atomic = "8")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "8"))] atomic_int! { + cfg(target_has_atomic = "8"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), @@ -1922,8 +1927,9 @@ "AtomicU8::new(0)", u8 AtomicU8 ATOMIC_U8_INIT } -#[cfg(target_has_atomic = "16")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "16"))] atomic_int! { + cfg(target_has_atomic = "16"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), @@ -1938,8 +1944,9 @@ "AtomicI16::new(0)", i16 AtomicI16 ATOMIC_I16_INIT } -#[cfg(target_has_atomic = "16")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "16"))] atomic_int! { + cfg(target_has_atomic = "16"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), @@ -1954,8 +1961,9 @@ "AtomicU16::new(0)", u16 AtomicU16 ATOMIC_U16_INIT } -#[cfg(target_has_atomic = "32")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "32"))] atomic_int! { + cfg(target_has_atomic = "32"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), @@ -1970,8 +1978,9 @@ "AtomicI32::new(0)", i32 AtomicI32 ATOMIC_I32_INIT } -#[cfg(target_has_atomic = "32")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "32"))] atomic_int! { + cfg(target_has_atomic = "32"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), @@ -1986,8 +1995,12 @@ "AtomicU32::new(0)", u32 AtomicU32 ATOMIC_U32_INIT } -#[cfg(target_has_atomic = "64")] +#[cfg(any( + all(bootstrap, target_has_atomic = "64"), + target_has_atomic_load_store = "64" +))] atomic_int! { + cfg(target_has_atomic = "64"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), @@ -2002,8 +2015,12 @@ "AtomicI64::new(0)", i64 AtomicI64 ATOMIC_I64_INIT } -#[cfg(target_has_atomic = "64")] +#[cfg(any( + all(bootstrap, target_has_atomic = "64"), + target_has_atomic_load_store = "64" +))] atomic_int! { + cfg(target_has_atomic = "64"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), @@ -2018,8 +2035,9 @@ "AtomicU64::new(0)", u64 AtomicU64 ATOMIC_U64_INIT } -#[cfg(target_has_atomic = "128")] +#[cfg(target_has_atomic_load_store = "128")] atomic_int! { + cfg(target_has_atomic = "128"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), @@ -2034,8 +2052,9 @@ "AtomicI128::new(0)", i128 AtomicI128 ATOMIC_I128_INIT } -#[cfg(target_has_atomic = "128")] +#[cfg(target_has_atomic_load_store = "128")] atomic_int! { + cfg(target_has_atomic = "128"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), @@ -2050,20 +2069,24 @@ "AtomicU128::new(0)", u128 AtomicU128 ATOMIC_U128_INIT } +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] #[cfg(target_pointer_width = "16")] macro_rules! ptr_width { () => { 2 } } +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] #[cfg(target_pointer_width = "32")] macro_rules! ptr_width { () => { 4 } } +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] #[cfg(target_pointer_width = "64")] macro_rules! ptr_width { () => { 8 } } -#[cfg(target_has_atomic = "ptr")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] atomic_int!{ + cfg(target_has_atomic = "ptr"), stable(feature = "rust1", since = "1.0.0"), stable(feature = "extended_compare_and_swap", since = "1.10.0"), stable(feature = "atomic_debug", since = "1.3.0"), @@ -2078,8 +2101,9 @@ "AtomicIsize::new(0)", isize AtomicIsize ATOMIC_ISIZE_INIT } -#[cfg(target_has_atomic = "ptr")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] atomic_int!{ + cfg(target_has_atomic = "ptr"), stable(feature = "rust1", since = "1.0.0"), stable(feature = "extended_compare_and_swap", since = "1.10.0"), stable(feature = "atomic_debug", since = "1.3.0"), @@ -2096,7 +2120,7 @@ } #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] fn strongest_failure_ordering(order: Ordering) -> Ordering { match order { Release => Relaxed, @@ -2130,7 +2154,7 @@ } #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_swap<T>(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xchg_acq(dst, val), @@ -2143,7 +2167,7 @@ /// Returns the previous value (like __sync_fetch_and_add). #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_add<T>(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xadd_acq(dst, val), @@ -2156,7 +2180,7 @@ /// Returns the previous value (like __sync_fetch_and_sub). #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_sub<T>(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xsub_acq(dst, val), @@ -2168,7 +2192,7 @@ } #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_compare_exchange<T>(dst: *mut T, old: T, new: T, @@ -2193,7 +2217,7 @@ } #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_compare_exchange_weak<T>(dst: *mut T, old: T, new: T, @@ -2218,7 +2242,7 @@ } #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_and<T>(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_and_acq(dst, val), @@ -2230,7 +2254,7 @@ } #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_nand<T>(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_nand_acq(dst, val), @@ -2242,7 +2266,7 @@ } #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_or<T>(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_or_acq(dst, val), @@ -2254,7 +2278,7 @@ } #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_xor<T>(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xor_acq(dst, val), @@ -2267,7 +2291,7 @@ /// returns the max value (signed comparison) #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_max<T>(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_max_acq(dst, val), @@ -2280,7 +2304,7 @@ /// returns the min value (signed comparison) #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_min<T>(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_min_acq(dst, val), @@ -2293,7 +2317,7 @@ /// returns the max value (signed comparison) #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_umax<T>(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_umax_acq(dst, val), @@ -2306,7 +2330,7 @@ /// returns the min value (signed comparison) #[inline] -#[cfg(target_has_atomic = "cas")] +#[cfg(target_has_atomic = "8")] unsafe fn atomic_umin<T>(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_umin_acq(dst, val), @@ -2504,7 +2528,7 @@ } -#[cfg(target_has_atomic = "8")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "8"))] #[stable(feature = "atomic_debug", since = "1.3.0")] impl fmt::Debug for AtomicBool { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -2512,7 +2536,7 @@ } } -#[cfg(target_has_atomic = "ptr")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] #[stable(feature = "atomic_debug", since = "1.3.0")] impl<T> fmt::Debug for AtomicPtr<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -2520,7 +2544,7 @@ } } -#[cfg(target_has_atomic = "ptr")] +#[cfg(any(bootstrap, target_has_atomic_load_store = "ptr"))] #[stable(feature = "atomic_pointer", since = "1.24.0")] impl<T> fmt::Pointer for AtomicPtr<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/src/libcore/task/wake.rs b/src/libcore/task/wake.rs index 65e2936..6f841bd 100644 --- a/src/libcore/task/wake.rs +++ b/src/libcore/task/wake.rs
@@ -217,7 +217,7 @@ /// This handle encapsulates a [`RawWaker`] instance, which defines the /// executor-specific wakeup behavior. /// -/// Implements [`Clone`], [`trait@Send`], and [`trait@Sync`]. +/// Implements [`Clone`], [`Send`], and [`Sync`]. /// /// [`RawWaker`]: struct.RawWaker.html #[repr(transparent)]
diff --git a/src/libcore/tests/array.rs b/src/libcore/tests/array.rs index 9e133ac..4f3b79c 100644 --- a/src/libcore/tests/array.rs +++ b/src/libcore/tests/array.rs
@@ -1,4 +1,4 @@ -use core::array::FixedSizeArray; +use core::array::{FixedSizeArray, IntoIter}; use core::convert::TryFrom; #[test] @@ -40,3 +40,208 @@ 30 31 32 } } + + +#[test] +fn iterator_collect() { + let arr = [0, 1, 2, 5, 9]; + let v: Vec<_> = IntoIter::new(arr.clone()).collect(); + assert_eq!(&arr[..], &v[..]); +} + +#[test] +fn iterator_rev_collect() { + let arr = [0, 1, 2, 5, 9]; + let v: Vec<_> = IntoIter::new(arr.clone()).rev().collect(); + assert_eq!(&v[..], &[9, 5, 2, 1, 0]); +} + +#[test] +fn iterator_nth() { + let v = [0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(IntoIter::new(v.clone()).nth(i).unwrap(), v[i]); + } + assert_eq!(IntoIter::new(v.clone()).nth(v.len()), None); + + let mut iter = IntoIter::new(v); + assert_eq!(iter.nth(2).unwrap(), v[2]); + assert_eq!(iter.nth(1).unwrap(), v[4]); +} + +#[test] +fn iterator_last() { + let v = [0, 1, 2, 3, 4]; + assert_eq!(IntoIter::new(v).last().unwrap(), 4); + assert_eq!(IntoIter::new([0]).last().unwrap(), 0); + + let mut it = IntoIter::new([0, 9, 2, 4]); + assert_eq!(it.next_back(), Some(4)); + assert_eq!(it.last(), Some(2)); +} + +#[test] +fn iterator_clone() { + let mut it = IntoIter::new([0, 2, 4, 6, 8]); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let mut clone = it.clone(); + assert_eq!(it.next_back(), Some(6)); + assert_eq!(clone.next_back(), Some(6)); + assert_eq!(it.next_back(), Some(4)); + assert_eq!(clone.next_back(), Some(4)); + assert_eq!(it.next(), Some(2)); + assert_eq!(clone.next(), Some(2)); +} + +#[test] +fn iterator_fused() { + let mut it = IntoIter::new([0, 9, 2]); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next(), Some(9)); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.next(), None); + assert_eq!(it.next(), None); + assert_eq!(it.next(), None); + assert_eq!(it.next(), None); + assert_eq!(it.next(), None); +} + +#[test] +fn iterator_len() { + let mut it = IntoIter::new([0, 1, 2, 5, 9]); + assert_eq!(it.size_hint(), (5, Some(5))); + assert_eq!(it.len(), 5); + assert_eq!(it.is_empty(), false); + + assert_eq!(it.next(), Some(0)); + assert_eq!(it.size_hint(), (4, Some(4))); + assert_eq!(it.len(), 4); + assert_eq!(it.is_empty(), false); + + assert_eq!(it.next_back(), Some(9)); + assert_eq!(it.size_hint(), (3, Some(3))); + assert_eq!(it.len(), 3); + assert_eq!(it.is_empty(), false); + + // Empty + let it = IntoIter::new([] as [String; 0]); + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.len(), 0); + assert_eq!(it.is_empty(), true); +} + +#[test] +fn iterator_count() { + let v = [0, 1, 2, 3, 4]; + assert_eq!(IntoIter::new(v.clone()).count(), 5); + + let mut iter2 = IntoIter::new(v); + iter2.next(); + iter2.next(); + assert_eq!(iter2.count(), 3); +} + +#[test] +fn iterator_flat_map() { + assert!((0..5).flat_map(|i| IntoIter::new([2 * i, 2 * i + 1])).eq(0..10)); +} + +#[test] +fn iterator_debug() { + let arr = [0, 1, 2, 5, 9]; + assert_eq!( + format!("{:?}", IntoIter::new(arr)), + "IntoIter([0, 1, 2, 5, 9])", + ); +} + +#[test] +fn iterator_drops() { + use core::cell::Cell; + + // This test makes sure the correct number of elements are dropped. The `R` + // type is just a reference to a `Cell` that is incremented when an `R` is + // dropped. + + #[derive(Clone)] + struct Foo<'a>(&'a Cell<usize>); + + impl Drop for Foo<'_> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1); + } + } + + fn five(i: &Cell<usize>) -> [Foo<'_>; 5] { + // This is somewhat verbose because `Foo` does not implement `Copy` + // since it implements `Drop`. Consequently, we cannot write + // `[Foo(i); 5]`. + [Foo(i), Foo(i), Foo(i), Foo(i), Foo(i)] + } + + // Simple: drop new iterator. + let i = Cell::new(0); + { + IntoIter::new(five(&i)); + } + assert_eq!(i.get(), 5); + + // Call `next()` once. + let i = Cell::new(0); + { + let mut iter = IntoIter::new(five(&i)); + let _x = iter.next(); + assert_eq!(i.get(), 0); + assert_eq!(iter.count(), 4); + assert_eq!(i.get(), 4); + } + assert_eq!(i.get(), 5); + + // Check `clone` and calling `next`/`next_back`. + let i = Cell::new(0); + { + let mut iter = IntoIter::new(five(&i)); + iter.next(); + assert_eq!(i.get(), 1); + iter.next_back(); + assert_eq!(i.get(), 2); + + let mut clone = iter.clone(); + assert_eq!(i.get(), 2); + + iter.next(); + assert_eq!(i.get(), 3); + + clone.next(); + assert_eq!(i.get(), 4); + + assert_eq!(clone.count(), 2); + assert_eq!(i.get(), 6); + } + assert_eq!(i.get(), 8); + + // Check via `nth`. + let i = Cell::new(0); + { + let mut iter = IntoIter::new(five(&i)); + let _x = iter.nth(2); + assert_eq!(i.get(), 2); + let _y = iter.last(); + assert_eq!(i.get(), 3); + } + assert_eq!(i.get(), 5); + + // Check every element. + let i = Cell::new(0); + for (index, _x) in IntoIter::new(five(&i)).enumerate() { + assert_eq!(i.get(), index); + } + assert_eq!(i.get(), 5); + + let i = Cell::new(0); + for (index, _x) in IntoIter::new(five(&i)).rev().enumerate() { + assert_eq!(i.get(), index); + } + assert_eq!(i.get(), 5); +}
diff --git a/src/libcore/tests/fmt/builders.rs b/src/libcore/tests/fmt/builders.rs index 200659b..2557244 100644 --- a/src/libcore/tests/fmt/builders.rs +++ b/src/libcore/tests/fmt/builders.rs
@@ -320,6 +320,46 @@ } #[test] + fn test_entry_err() { + // Ensure errors in a map entry don't trigger panics (#65231) + use std::fmt::Write; + + struct ErrorFmt; + + impl fmt::Debug for ErrorFmt { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + Err(fmt::Error) + } + } + + struct KeyValue<K, V>(usize, K, V); + + impl<K, V> fmt::Debug for KeyValue<K, V> + where + K: fmt::Debug, + V: fmt::Debug, + { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut map = fmt.debug_map(); + + for _ in 0..self.0 { + map.entry(&self.1, &self.2); + } + + map.finish() + } + } + + let mut buf = String::new(); + + assert!(write!(&mut buf, "{:?}", KeyValue(1, ErrorFmt, "bar")).is_err()); + assert!(write!(&mut buf, "{:?}", KeyValue(1, "foo", ErrorFmt)).is_err()); + + assert!(write!(&mut buf, "{:?}", KeyValue(2, ErrorFmt, "bar")).is_err()); + assert!(write!(&mut buf, "{:?}", KeyValue(2, "foo", ErrorFmt)).is_err()); + } + + #[test] #[should_panic] fn test_invalid_key_when_entry_is_incomplete() { struct Foo;
diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 3566135..b28ed2e 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs
@@ -31,6 +31,7 @@ #![feature(slice_partition_dedup)] #![feature(int_error_matching)] #![feature(const_fn)] +#![feature(array_value_iter)] #![feature(iter_partition_in_place)] #![feature(iter_is_partitioned)] #![feature(iter_order_by)]
diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index f9c1be2..d22420e 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs
@@ -410,7 +410,7 @@ &self.input[start..self.input.len()] } - /// Parses an Argument structure, or what's contained within braces inside the format string + /// Parses an `Argument` structure, or what's contained within braces inside the format string. fn argument(&mut self) -> Argument<'a> { let pos = self.position(); let format = self.format(); @@ -464,7 +464,7 @@ } /// Parses a format specifier at the current position, returning all of the - /// relevant information in the FormatSpec struct. + /// relevant information in the `FormatSpec` struct. fn format(&mut self) -> FormatSpec<'a> { let mut spec = FormatSpec { fill: None, @@ -571,7 +571,7 @@ spec } - /// Parses a Count parameter at the current position. This does not check + /// Parses a `Count` parameter at the current position. This does not check /// for 'CountIsNextParam' because that is only used in precision, not /// width. fn count(&mut self, start: usize) -> (Count, Option<InnerSpan>) {
diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index fd144d6..5509f47 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs
@@ -54,7 +54,8 @@ core::intrinsics::abort(); } - #[cfg(all(target_vendor="fortanix", target_env="sgx"))] + #[cfg(any(target_os = "hermit", + all(target_vendor="fortanix", target_env="sgx")))] unsafe fn abort() -> ! { // call std::sys::abort_internal extern "C" { pub fn __rust_abort() -> !; }
diff --git a/src/libpanic_unwind/dwarf/eh.rs b/src/libpanic_unwind/dwarf/eh.rs index 07fa297..1e9e7e4 100644 --- a/src/libpanic_unwind/dwarf/eh.rs +++ b/src/libpanic_unwind/dwarf/eh.rs
@@ -51,7 +51,7 @@ pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm")); -pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) +pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>, foreign_exception: bool) -> Result<EHAction, ()> { if lsda.is_null() { @@ -96,7 +96,7 @@ return Ok(EHAction::None) } else { let lpad = lpad_base + cs_lpad; - return Ok(interpret_cs_action(cs_action, lpad)) + return Ok(interpret_cs_action(cs_action, lpad, foreign_exception)) } } } @@ -121,16 +121,23 @@ // Can never have null landing pad for sjlj -- that would have // been indicated by a -1 call site index. let lpad = (cs_lpad + 1) as usize; - return Ok(interpret_cs_action(cs_action, lpad)) + return Ok(interpret_cs_action(cs_action, lpad, foreign_exception)) } } } } -fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction { +fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) -> EHAction { if cs_action == 0 { + // If cs_action is 0 then this is a cleanup (Drop::drop). We run these + // for both Rust panics and foriegn exceptions. EHAction::Cleanup(lpad) + } else if foreign_exception { + // catch_unwind should not catch foreign exceptions, only Rust panics. + // Instead just continue unwinding. + EHAction::None } else { + // Stop unwinding Rust panics at catch_unwind. EHAction::Catch(lpad) } }
diff --git a/src/libpanic_unwind/emcc.rs b/src/libpanic_unwind/emcc.rs index 18e9006..3d0d5a4 100644 --- a/src/libpanic_unwind/emcc.rs +++ b/src/libpanic_unwind/emcc.rs
@@ -15,14 +15,48 @@ use libc::{self, c_int}; use unwind as uw; +// This matches the layout of std::type_info in C++ +#[repr(C)] +struct TypeInfo { + vtable: *const usize, + name: *const u8, +} +unsafe impl Sync for TypeInfo {} + +extern "C" { + // The leading `\x01` byte here is actually a magical signal to LLVM to + // *not* apply any other mangling like prefixing with a `_` character. + // + // This symbol is the vtable used by C++'s `std::type_info`. Objects of type + // `std::type_info`, type descriptors, have a pointer to this table. Type + // descriptors are referenced by the C++ EH structures defined above and + // that we construct below. + // + // Note that the real size is larger than 3 usize, but we only need our + // vtable to point to the third element. + #[link_name = "\x01_ZTVN10__cxxabiv117__class_type_infoE"] + static CLASS_TYPE_INFO_VTABLE: [usize; 3]; +} + +// std::type_info for a rust_panic class +#[lang = "eh_catch_typeinfo"] +static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo { + // Normally we would use .as_ptr().add(2) but this doesn't work in a const context. + vtable: unsafe { &CLASS_TYPE_INFO_VTABLE[2] }, + // This intentionally doesn't use the normal name mangling scheme because + // we don't want C++ to be able to produce or catch Rust panics. + name: b"rust_panic\0".as_ptr(), +}; + pub fn payload() -> *mut u8 { ptr::null_mut() } pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> { assert!(!ptr.is_null()); - let ex = ptr::read(ptr as *mut _); - __cxa_free_exception(ptr as *mut _); + let adjusted_ptr = __cxa_begin_catch(ptr as *mut libc::c_void); + let ex = ptr::read(adjusted_ptr as *mut _); + __cxa_end_catch(); ex } @@ -32,11 +66,8 @@ if exception == ptr::null_mut() { return uw::_URC_FATAL_PHASE1_ERROR as u32; } - let exception = exception as *mut Box<dyn Any + Send>; - ptr::write(exception, data); - __cxa_throw(exception as *mut _, ptr::null_mut(), ptr::null_mut()); - - unreachable!() + ptr::write(exception as *mut _, data); + __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, ptr::null_mut()); } #[lang = "eh_personality"] @@ -52,10 +83,11 @@ extern "C" { fn __cxa_allocate_exception(thrown_size: libc::size_t) -> *mut libc::c_void; - fn __cxa_free_exception(thrown_exception: *mut libc::c_void); + fn __cxa_begin_catch(thrown_exception: *mut libc::c_void) -> *mut libc::c_void; + fn __cxa_end_catch(); fn __cxa_throw(thrown_exception: *mut libc::c_void, - tinfo: *mut libc::c_void, - dest: *mut libc::c_void); + tinfo: *const TypeInfo, + dest: *mut libc::c_void) -> !; fn __gxx_personality_v0(version: c_int, actions: uw::_Unwind_Action, exception_class: uw::_Unwind_Exception_Class,
diff --git a/src/libpanic_unwind/gcc.rs b/src/libpanic_unwind/gcc.rs index 236ed15..4f572fe 100644 --- a/src/libpanic_unwind/gcc.rs +++ b/src/libpanic_unwind/gcc.rs
@@ -133,133 +133,176 @@ // https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c -// The personality routine for most of our targets, except ARM, which has a slightly different ABI -// (however, iOS goes here as it uses SjLj unwinding). Also, the 64-bit Windows implementation -// lives in seh64_gnu.rs -#[cfg(all(any(target_os = "ios", target_os = "netbsd", not(target_arch = "arm"))))] -#[lang = "eh_personality"] -#[no_mangle] -#[allow(unused)] -unsafe extern "C" fn rust_eh_personality(version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code { - if version != 1 { - return uw::_URC_FATAL_PHASE1_ERROR; - } - let eh_action = match find_eh_action(context) { - Ok(action) => action, - Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, - }; - if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { - match eh_action { - EHAction::None | - EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND, - EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND, - EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR, +cfg_if::cfg_if! { + if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "netbsd")))] { + // ARM EHABI personality routine. + // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf + // + // iOS uses the default routine instead since it uses SjLj unwinding. + #[lang = "eh_personality"] + #[no_mangle] + unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + let state = state as c_int; + let action = state & uw::_US_ACTION_MASK as c_int; + let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { + // Backtraces on ARM will call the personality routine with + // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases + // we want to continue unwinding the stack, otherwise all our backtraces + // would end at __rust_try + if state & uw::_US_FORCE_UNWIND as c_int != 0 { + return continue_unwind(exception_object, context); + } + true + } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { + false + } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { + return continue_unwind(exception_object, context); + } else { + return uw::_URC_FAILURE; + }; + + // The DWARF unwinder assumes that _Unwind_Context holds things like the function + // and LSDA pointers, however ARM EHABI places them into the exception object. + // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which + // take only the context pointer, GCC personality routines stash a pointer to + // exception_object in the context, using location reserved for ARM's + // "scratch register" (r12). + uw::_Unwind_SetGR(context, + uw::UNWIND_POINTER_REG, + exception_object as uw::_Unwind_Ptr); + // ...A more principled approach would be to provide the full definition of ARM's + // _Unwind_Context in our libunwind bindings and fetch the required data from there + // directly, bypassing DWARF compatibility functions. + + let exception_class = (*exception_object).exception_class; + let foreign_exception = exception_class != rust_exception_class(); + let eh_action = match find_eh_action(context, foreign_exception) { + Ok(action) => action, + Err(_) => return uw::_URC_FAILURE, + }; + if search_phase { + match eh_action { + EHAction::None | + EHAction::Cleanup(_) => return continue_unwind(exception_object, context), + EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND, + EHAction::Terminate => return uw::_URC_FAILURE, + } + } else { + match eh_action { + EHAction::None => return continue_unwind(exception_object, context), + EHAction::Cleanup(lpad) | + EHAction::Catch(lpad) => { + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, + exception_object as uintptr_t); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); + uw::_Unwind_SetIP(context, lpad); + return uw::_URC_INSTALL_CONTEXT; + } + EHAction::Terminate => return uw::_URC_FAILURE, + } + } + + // On ARM EHABI the personality routine is responsible for actually + // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). + unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { + uw::_URC_CONTINUE_UNWIND + } else { + uw::_URC_FAILURE + } + } + // defined in libgcc + extern "C" { + fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } } } else { - match eh_action { - EHAction::None => return uw::_URC_CONTINUE_UNWIND, - EHAction::Cleanup(lpad) | - EHAction::Catch(lpad) => { - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t); - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); - uw::_Unwind_SetIP(context, lpad); - return uw::_URC_INSTALL_CONTEXT; + // Default personality routine, which is used directly on most targets + // and indirectly on Windows x86_64 via SEH. + unsafe extern "C" fn rust_eh_personality_impl(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + if version != 1 { + return uw::_URC_FATAL_PHASE1_ERROR; } - EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR, + let foreign_exception = exception_class != rust_exception_class(); + let eh_action = match find_eh_action(context, foreign_exception) { + Ok(action) => action, + Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, + }; + if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { + match eh_action { + EHAction::None | + EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND, + EHAction::Catch(_) => uw::_URC_HANDLER_FOUND, + EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR, + } + } else { + match eh_action { + EHAction::None => uw::_URC_CONTINUE_UNWIND, + EHAction::Cleanup(lpad) | + EHAction::Catch(lpad) => { + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, + exception_object as uintptr_t); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); + uw::_Unwind_SetIP(context, lpad); + uw::_URC_INSTALL_CONTEXT + } + EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, + } + } + } + + cfg_if::cfg_if! { + if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] { + // On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind + // handler data (aka LSDA) uses GCC-compatible encoding. + #[lang = "eh_personality"] + #[no_mangle] + #[allow(nonstandard_style)] + unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut uw::EXCEPTION_RECORD, + establisherFrame: uw::LPVOID, + contextRecord: *mut uw::CONTEXT, + dispatcherContext: *mut uw::DISPATCHER_CONTEXT) + -> uw::EXCEPTION_DISPOSITION { + uw::_GCC_specific_handler(exceptionRecord, + establisherFrame, + contextRecord, + dispatcherContext, + rust_eh_personality_impl) + } + } else { + // The personality routine for most of our targets. + #[lang = "eh_personality"] + #[no_mangle] + unsafe extern "C" fn rust_eh_personality(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + rust_eh_personality_impl(version, + actions, + exception_class, + exception_object, + context) + } + } } } } -// ARM EHABI personality routine. -// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf -#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "netbsd")))] -#[lang = "eh_personality"] -#[no_mangle] -unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State, - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code { - let state = state as c_int; - let action = state & uw::_US_ACTION_MASK as c_int; - let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { - // Backtraces on ARM will call the personality routine with - // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases - // we want to continue unwinding the stack, otherwise all our backtraces - // would end at __rust_try - if state & uw::_US_FORCE_UNWIND as c_int != 0 { - return continue_unwind(exception_object, context); - } - true - } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { - false - } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { - return continue_unwind(exception_object, context); - } else { - return uw::_URC_FAILURE; - }; - - // The DWARF unwinder assumes that _Unwind_Context holds things like the function - // and LSDA pointers, however ARM EHABI places them into the exception object. - // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which - // take only the context pointer, GCC personality routines stash a pointer to exception_object - // in the context, using location reserved for ARM's "scratch register" (r12). - uw::_Unwind_SetGR(context, - uw::UNWIND_POINTER_REG, - exception_object as uw::_Unwind_Ptr); - // ...A more principled approach would be to provide the full definition of ARM's - // _Unwind_Context in our libunwind bindings and fetch the required data from there directly, - // bypassing DWARF compatibility functions. - - let eh_action = match find_eh_action(context) { - Ok(action) => action, - Err(_) => return uw::_URC_FAILURE, - }; - if search_phase { - match eh_action { - EHAction::None | - EHAction::Cleanup(_) => return continue_unwind(exception_object, context), - EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND, - EHAction::Terminate => return uw::_URC_FAILURE, - } - } else { - match eh_action { - EHAction::None => return continue_unwind(exception_object, context), - EHAction::Cleanup(lpad) | - EHAction::Catch(lpad) => { - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t); - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); - uw::_Unwind_SetIP(context, lpad); - return uw::_URC_INSTALL_CONTEXT; - } - EHAction::Terminate => return uw::_URC_FAILURE, - } - } - - // On ARM EHABI the personality routine is responsible for actually - // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). - unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code { - if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { - uw::_URC_CONTINUE_UNWIND - } else { - uw::_URC_FAILURE - } - } - // defined in libgcc - extern "C" { - fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code; - } -} - -unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) +unsafe fn find_eh_action(context: *mut uw::_Unwind_Context, foreign_exception: bool) -> Result<EHAction, ()> { let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; @@ -273,11 +316,11 @@ get_text_start: &|| uw::_Unwind_GetTextRelBase(context), get_data_start: &|| uw::_Unwind_GetDataRelBase(context), }; - eh::find_eh_action(lsda, &eh_context) + eh::find_eh_action(lsda, &eh_context, foreign_exception) } // See docs in the `unwind` module. -#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] +#[cfg(all(target_os="windows", any(target_arch = "x86", target_arch = "x86_64"), target_env="gnu"))] #[lang = "eh_unwind_resume"] #[unwind(allowed)] unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! {
diff --git a/src/libpanic_unwind/hermit.rs b/src/libpanic_unwind/hermit.rs new file mode 100644 index 0000000..8bee6ff --- /dev/null +++ b/src/libpanic_unwind/hermit.rs
@@ -0,0 +1,21 @@ +//! Unwinding for *hermit* target. +//! +//! Right now we don't support this, so this is just stubs. + +use alloc::boxed::Box; +use core::ptr; +use core::any::Any; + +pub fn payload() -> *mut u8 { + ptr::null_mut() +} + +pub unsafe fn cleanup(_ptr: *mut u8) -> Box<dyn Any + Send> { + extern "C" { pub fn __rust_abort() -> !; } + __rust_abort(); +} + +pub unsafe fn panic(_data: Box<dyn Any + Send>) -> u32 { + extern "C" { pub fn __rust_abort() -> !; } + __rust_abort(); +}
diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index 06e6e76..d2a0ef7 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs
@@ -5,9 +5,8 @@ //! essentially gets categorized into three buckets currently: //! //! 1. MSVC targets use SEH in the `seh.rs` file. -//! 2. The 64-bit MinGW target half-uses SEH and half-use gcc-like information -//! in the `seh64_gnu.rs` module. -//! 3. All other targets use libunwind/libgcc in the `gcc/mod.rs` module. +//! 2. Emscripten uses C++ exceptions in the `emcc.rs` file. +//! 3. All other targets use libunwind/libgcc in the `gcc.rs` file. //! //! More documentation about each implementation can be found in the respective //! module. @@ -43,15 +42,15 @@ } else if #[cfg(target_arch = "wasm32")] { #[path = "dummy.rs"] mod imp; + } else if #[cfg(target_os = "hermit")] { + #[path = "hermit.rs"] + mod imp; } else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] { #[path = "dummy.rs"] mod imp; } else if #[cfg(target_env = "msvc")] { #[path = "seh.rs"] mod imp; - } else if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] { - #[path = "seh64_gnu.rs"] - mod imp; } else { // Rust runtime's startup objects depend on these symbols, so make them public. #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] @@ -62,7 +61,6 @@ } mod dwarf; -mod windows; // Entry point for catching an exception, implemented using the `try` intrinsic // in the compiler.
diff --git a/src/libpanic_unwind/seh.rs b/src/libpanic_unwind/seh.rs index 809e461..621813a 100644 --- a/src/libpanic_unwind/seh.rs +++ b/src/libpanic_unwind/seh.rs
@@ -51,9 +51,7 @@ use core::any::Any; use core::mem; use core::raw; - -use crate::windows as c; -use libc::{c_int, c_uint}; +use libc::{c_int, c_uint, c_void}; // First up, a whole bunch of type definitions. There's a few platform-specific // oddities here, and a lot that's just blatantly copied from LLVM. The purpose @@ -76,18 +74,19 @@ // sort of operation. For example, if you compile this C++ code on MSVC and emit // the LLVM IR: // -// #include <stdin.h> +// #include <stdint.h> +// +// struct rust_panic { +// uint64_t x[2]; +// } // // void foo() { -// uint64_t a[2] = {0, 1}; +// rust_panic a = {0, 1}; // throw a; // } // // That's essentially what we're trying to emulate. Most of the constant values -// below were just copied from LLVM, I'm at least not 100% sure what's going on -// everywhere. For example the `.PA_K\0` and `.PEA_K\0` strings below (stuck in -// the names of a few of these) I'm not actually sure what they do, but it seems -// to mirror what LLVM does! +// below were just copied from LLVM, // // In any case, these structures are all constructed in a similar manner, and // it's just somewhat verbose for us. @@ -98,10 +97,9 @@ #[macro_use] mod imp { pub type ptr_t = *mut u8; - pub const OFFSET: i32 = 4; + #[cfg(bootstrap)] pub const NAME1: [u8; 7] = [b'.', b'P', b'A', b'_', b'K', 0, 0]; - pub const NAME2: [u8; 7] = [b'.', b'P', b'A', b'X', 0, 0, 0]; macro_rules! ptr { (0) => (core::ptr::null_mut()); @@ -113,10 +111,9 @@ #[macro_use] mod imp { pub type ptr_t = u32; - pub const OFFSET: i32 = 8; + #[cfg(bootstrap)] pub const NAME1: [u8; 7] = [b'.', b'P', b'E', b'A', b'_', b'K', 0]; - pub const NAME2: [u8; 7] = [b'.', b'P', b'E', b'A', b'X', 0, 0]; extern "C" { pub static __ImageBase: u8; @@ -141,7 +138,7 @@ #[repr(C)] pub struct _CatchableTypeArray { pub nCatchableTypes: c_int, - pub arrayOfCatchableTypes: [imp::ptr_t; 2], + pub arrayOfCatchableTypes: [imp::ptr_t; 1], } #[repr(C)] @@ -164,9 +161,19 @@ pub struct _TypeDescriptor { pub pVFTable: *const u8, pub spare: *mut u8, + #[cfg(bootstrap)] pub name: [u8; 7], + #[cfg(not(bootstrap))] + pub name: [u8; 11], } +// Note that we intentionally ignore name mangling rules here: we don't want C++ +// to be able to catch Rust panics by simply declaring a `struct rust_panic`. +#[cfg(bootstrap)] +use imp::NAME1 as TYPE_NAME; +#[cfg(not(bootstrap))] +const TYPE_NAME: [u8; 11] = *b"rust_panic\0"; + static mut THROW_INFO: _ThrowInfo = _ThrowInfo { attributes: 0, pnfnUnwind: ptr!(0), @@ -175,31 +182,22 @@ }; static mut CATCHABLE_TYPE_ARRAY: _CatchableTypeArray = _CatchableTypeArray { - nCatchableTypes: 2, - arrayOfCatchableTypes: [ptr!(0), ptr!(0)], + nCatchableTypes: 1, + arrayOfCatchableTypes: [ptr!(0)], }; -static mut CATCHABLE_TYPE1: _CatchableType = _CatchableType { - properties: 1, +static mut CATCHABLE_TYPE: _CatchableType = _CatchableType { + properties: 0, pType: ptr!(0), thisDisplacement: _PMD { mdisp: 0, pdisp: -1, vdisp: 0, }, - sizeOrOffset: imp::OFFSET, - copy_function: ptr!(0), -}; - -static mut CATCHABLE_TYPE2: _CatchableType = _CatchableType { - properties: 1, - pType: ptr!(0), - thisDisplacement: _PMD { - mdisp: 0, - pdisp: -1, - vdisp: 0, - }, - sizeOrOffset: imp::OFFSET, + #[cfg(bootstrap)] + sizeOrOffset: mem::size_of::<*mut u64>() as c_int, + #[cfg(not(bootstrap))] + sizeOrOffset: mem::size_of::<[u64; 2]>() as c_int, copy_function: ptr!(0), }; @@ -215,22 +213,17 @@ static TYPE_INFO_VTABLE: *const u8; } -// We use #[lang = "msvc_try_filter"] here as this is the type descriptor which +// We use #[lang = "eh_catch_typeinfo"] here as this is the type descriptor which // we'll use in LLVM's `catchpad` instruction which ends up also being passed as // an argument to the C++ personality function. // // Again, I'm not entirely sure what this is describing, it just seems to work. -#[cfg_attr(not(test), lang = "msvc_try_filter")] -static mut TYPE_DESCRIPTOR1: _TypeDescriptor = _TypeDescriptor { +#[cfg_attr(bootstrap, lang = "msvc_try_filter")] +#[cfg_attr(not(any(test, bootstrap)), lang = "eh_catch_typeinfo")] +static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor { pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _, spare: core::ptr::null_mut(), - name: imp::NAME1, -}; - -static mut TYPE_DESCRIPTOR2: _TypeDescriptor = _TypeDescriptor { - pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _, - spare: core::ptr::null_mut(), - name: imp::NAME2, + name: TYPE_NAME, }; pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 { @@ -246,6 +239,11 @@ let ptrs = mem::transmute::<_, raw::TraitObject>(data); let mut ptrs = [ptrs.data as u64, ptrs.vtable as u64]; let mut ptrs_ptr = ptrs.as_mut_ptr(); + let throw_ptr = if cfg!(bootstrap) { + &mut ptrs_ptr as *mut _ as *mut _ + } else { + ptrs_ptr as *mut _ + }; // This... may seems surprising, and justifiably so. On 32-bit MSVC the // pointers between these structure are just that, pointers. On 64-bit MSVC, @@ -270,17 +268,17 @@ atomic_store(&mut THROW_INFO.pCatchableTypeArray as *mut _ as *mut u32, ptr!(&CATCHABLE_TYPE_ARRAY as *const _) as u32); atomic_store(&mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[0] as *mut _ as *mut u32, - ptr!(&CATCHABLE_TYPE1 as *const _) as u32); - atomic_store(&mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[1] as *mut _ as *mut u32, - ptr!(&CATCHABLE_TYPE2 as *const _) as u32); - atomic_store(&mut CATCHABLE_TYPE1.pType as *mut _ as *mut u32, - ptr!(&TYPE_DESCRIPTOR1 as *const _) as u32); - atomic_store(&mut CATCHABLE_TYPE2.pType as *mut _ as *mut u32, - ptr!(&TYPE_DESCRIPTOR2 as *const _) as u32); + ptr!(&CATCHABLE_TYPE as *const _) as u32); + atomic_store(&mut CATCHABLE_TYPE.pType as *mut _ as *mut u32, + ptr!(&TYPE_DESCRIPTOR as *const _) as u32); - c::_CxxThrowException(&mut ptrs_ptr as *mut _ as *mut _, - &mut THROW_INFO as *mut _ as *mut _); - u32::max_value() + extern "system" { + #[unwind(allowed)] + pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !; + } + + _CxxThrowException(throw_ptr, + &mut THROW_INFO as *mut _ as *mut _); } pub fn payload() -> [u64; 2] {
diff --git a/src/libpanic_unwind/seh64_gnu.rs b/src/libpanic_unwind/seh64_gnu.rs deleted file mode 100644 index 457ffcd..0000000 --- a/src/libpanic_unwind/seh64_gnu.rs +++ /dev/null
@@ -1,127 +0,0 @@ -//! Unwinding implementation of top of native Win64 SEH, -//! however the unwind handler data (aka LSDA) uses GCC-compatible encoding. - -#![allow(nonstandard_style)] -#![allow(private_no_mangle_fns)] - -use alloc::boxed::Box; - -use core::any::Any; -use core::intrinsics; -use core::ptr; -use crate::dwarf::eh::{EHContext, EHAction, find_eh_action}; -use crate::windows as c; - -// Define our exception codes: -// according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx, -// [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success) -// [29] = 1 (user-defined) -// [28] = 0 (reserved) -// we define bits: -// [24:27] = type -// [0:23] = magic -const ETYPE: c::DWORD = 0b1110_u32 << 28; -const MAGIC: c::DWORD = 0x525354; // "RST" - -const RUST_PANIC: c::DWORD = ETYPE | (1 << 24) | MAGIC; - -#[repr(C)] -struct PanicData { - data: Box<dyn Any + Send>, -} - -pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 { - let panic_ctx = Box::new(PanicData { data }); - let params = [Box::into_raw(panic_ctx) as c::ULONG_PTR]; - c::RaiseException(RUST_PANIC, - c::EXCEPTION_NONCONTINUABLE, - params.len() as c::DWORD, - ¶ms as *const c::ULONG_PTR); - u32::max_value() -} - -pub fn payload() -> *mut u8 { - ptr::null_mut() -} - -pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> { - let panic_ctx = Box::from_raw(ptr as *mut PanicData); - return panic_ctx.data; -} - -// SEH doesn't support resuming unwinds after calling a landing pad like -// libunwind does. For this reason, MSVC compiler outlines landing pads into -// separate functions that can be called directly from the personality function -// but are nevertheless able to find and modify stack frame of the "parent" -// function. -// -// Since this cannot be done with libdwarf-style landing pads, -// rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then -// reraises the exception. -// -// Note that it makes certain assumptions about the exception: -// -// 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to -// resume execution. -// 2. That the first parameter of the exception is a pointer to an extra data -// area (PanicData). -// Since these assumptions do not generally hold true for foreign exceptions -// (system faults, C++ exceptions, etc), we make no attempt to invoke our -// landing pads (and, thus, destructors!) for anything other than RUST_PANICs. -// This is considered acceptable, because the behavior of throwing exceptions -// through a C ABI boundary is undefined. - -#[lang = "eh_personality"] -#[cfg(not(test))] -unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut c::EXCEPTION_RECORD, - establisherFrame: c::LPVOID, - contextRecord: *mut c::CONTEXT, - dispatcherContext: *mut c::DISPATCHER_CONTEXT) - -> c::EXCEPTION_DISPOSITION { - let er = &*exceptionRecord; - let dc = &*dispatcherContext; - - if er.ExceptionFlags & c::EXCEPTION_UNWIND == 0 { - // we are in the dispatch phase - if er.ExceptionCode == RUST_PANIC { - if let Some(lpad) = find_landing_pad(dc) { - c::RtlUnwindEx(establisherFrame, - lpad as c::LPVOID, - exceptionRecord, - er.ExceptionInformation[0] as c::LPVOID, // pointer to PanicData - contextRecord, - dc.HistoryTable); - } - } - } - c::ExceptionContinueSearch -} - -#[lang = "eh_unwind_resume"] -#[unwind(allowed)] -unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! { - let params = [panic_ctx as c::ULONG_PTR]; - c::RaiseException(RUST_PANIC, - c::EXCEPTION_NONCONTINUABLE, - params.len() as c::DWORD, - ¶ms as *const c::ULONG_PTR); - intrinsics::abort(); -} - -unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option<usize> { - let eh_ctx = EHContext { - // The return address points 1 byte past the call instruction, - // which could be in the next IP range in LSDA range table. - ip: dc.ControlPc as usize - 1, - func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize, - get_text_start: &|| dc.ImageBase as usize, - get_data_start: &|| unimplemented!(), - }; - match find_eh_action(dc.HandlerData, &eh_ctx) { - Err(_) | - Ok(EHAction::None) => None, - Ok(EHAction::Cleanup(lpad)) | - Ok(EHAction::Catch(lpad)) => Some(lpad), - Ok(EHAction::Terminate) => intrinsics::abort(), - } -}
diff --git a/src/libpanic_unwind/windows.rs b/src/libpanic_unwind/windows.rs deleted file mode 100644 index 3257a9d..0000000 --- a/src/libpanic_unwind/windows.rs +++ /dev/null
@@ -1,86 +0,0 @@ -#![allow(nonstandard_style)] -#![allow(dead_code)] -#![cfg(windows)] - -use libc::{c_long, c_ulong, c_void}; - -pub type DWORD = c_ulong; -pub type LONG = c_long; -pub type ULONG_PTR = usize; -pub type LPVOID = *mut c_void; - -pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; -pub const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception -pub const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress -pub const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress -pub const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress -pub const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call -pub const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND | - EXCEPTION_TARGET_UNWIND | - EXCEPTION_COLLIDED_UNWIND; - -#[repr(C)] -pub struct EXCEPTION_RECORD { - pub ExceptionCode: DWORD, - pub ExceptionFlags: DWORD, - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ExceptionAddress: LPVOID, - pub NumberParameters: DWORD, - pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS], -} - -#[repr(C)] -pub struct EXCEPTION_POINTERS { - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ContextRecord: *mut CONTEXT, -} - -pub enum UNWIND_HISTORY_TABLE {} - -#[repr(C)] -pub struct RUNTIME_FUNCTION { - pub BeginAddress: DWORD, - pub EndAddress: DWORD, - pub UnwindData: DWORD, -} - -pub enum CONTEXT {} - -#[repr(C)] -pub struct DISPATCHER_CONTEXT { - pub ControlPc: LPVOID, - pub ImageBase: LPVOID, - pub FunctionEntry: *const RUNTIME_FUNCTION, - pub EstablisherFrame: LPVOID, - pub TargetIp: LPVOID, - pub ContextRecord: *const CONTEXT, - pub LanguageHandler: LPVOID, - pub HandlerData: *const u8, - pub HistoryTable: *const UNWIND_HISTORY_TABLE, -} - -#[repr(C)] -pub enum EXCEPTION_DISPOSITION { - ExceptionContinueExecution, - ExceptionContinueSearch, - ExceptionNestedException, - ExceptionCollidedUnwind, -} -pub use self::EXCEPTION_DISPOSITION::*; - -extern "system" { - #[unwind(allowed)] - pub fn RaiseException(dwExceptionCode: DWORD, - dwExceptionFlags: DWORD, - nNumberOfArguments: DWORD, - lpArguments: *const ULONG_PTR); - #[unwind(allowed)] - pub fn RtlUnwindEx(TargetFrame: LPVOID, - TargetIp: LPVOID, - ExceptionRecord: *const EXCEPTION_RECORD, - ReturnValue: LPVOID, - OriginalContext: *const CONTEXT, - HistoryTable: *const UNWIND_HISTORY_TABLE); - #[unwind(allowed)] - pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8); -}
diff --git a/src/libproc_macro/bridge/client.rs b/src/libproc_macro/bridge/client.rs index 5c54316..9643dba 100644 --- a/src/libproc_macro/bridge/client.rs +++ b/src/libproc_macro/bridge/client.rs
@@ -15,8 +15,9 @@ } impl HandleCounters { - // FIXME(#53451) public to work around `Cannot create local mono-item` ICE. - pub extern "C" fn get() -> &'static Self { + // FIXME(eddyb) use a reference to the `static COUNTERS`, intead of + // a wrapper `fn` pointer, once `const fn` can reference `static`s. + extern "C" fn get() -> &'static Self { static COUNTERS: HandleCounters = HandleCounters { $($oty: AtomicUsize::new(1),)* $($ity: AtomicUsize::new(1),)* @@ -333,16 +334,19 @@ #[repr(C)] #[derive(Copy, Clone)] pub struct Client<F> { + // FIXME(eddyb) use a reference to the `static COUNTERS`, intead of + // a wrapper `fn` pointer, once `const fn` can reference `static`s. pub(super) get_handle_counters: extern "C" fn() -> &'static HandleCounters, pub(super) run: extern "C" fn(Bridge<'_>, F) -> Buffer<u8>, pub(super) f: F, } -// FIXME(#53451) public to work around `Cannot create local mono-item` ICE, -// affecting not only the function itself, but also the `BridgeState` `thread_local!`. -pub extern "C" fn __run_expand1( +/// Client-side helper for handling client panics, entering the bridge, +/// deserializing input and serializing output. +// FIXME(eddyb) maybe replace `Bridge::enter` with this? +fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>( mut bridge: Bridge<'_>, - f: fn(crate::TokenStream) -> crate::TokenStream, + f: impl FnOnce(A) -> R, ) -> Buffer<u8> { // The initial `cached_buffer` contains the input. let mut b = bridge.cached_buffer.take(); @@ -350,12 +354,12 @@ panic::catch_unwind(panic::AssertUnwindSafe(|| { bridge.enter(|| { let reader = &mut &b[..]; - let input = TokenStream::decode(reader, &mut ()); + let input = A::decode(reader, &mut ()); // Put the `cached_buffer` back in the `Bridge`, for requests. Bridge::with(|bridge| bridge.cached_buffer = b.take()); - let output = f(crate::TokenStream(input)).0; + let output = f(input); // Take the `cached_buffer` back out, for the output value. b = Bridge::with(|bridge| bridge.cached_buffer.take()); @@ -383,65 +387,35 @@ impl Client<fn(crate::TokenStream) -> crate::TokenStream> { pub const fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self { + extern "C" fn run( + bridge: Bridge<'_>, + f: impl FnOnce(crate::TokenStream) -> crate::TokenStream, + ) -> Buffer<u8> { + run_client(bridge, |input| f(crate::TokenStream(input)).0) + } Client { get_handle_counters: HandleCounters::get, - run: __run_expand1, + run, f, } } } -// FIXME(#53451) public to work around `Cannot create local mono-item` ICE, -// affecting not only the function itself, but also the `BridgeState` `thread_local!`. -pub extern "C" fn __run_expand2( - mut bridge: Bridge<'_>, - f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, -) -> Buffer<u8> { - // The initial `cached_buffer` contains the input. - let mut b = bridge.cached_buffer.take(); - - panic::catch_unwind(panic::AssertUnwindSafe(|| { - bridge.enter(|| { - let reader = &mut &b[..]; - let input = TokenStream::decode(reader, &mut ()); - let input2 = TokenStream::decode(reader, &mut ()); - - // Put the `cached_buffer` back in the `Bridge`, for requests. - Bridge::with(|bridge| bridge.cached_buffer = b.take()); - - let output = f(crate::TokenStream(input), crate::TokenStream(input2)).0; - - // Take the `cached_buffer` back out, for the output value. - b = Bridge::with(|bridge| bridge.cached_buffer.take()); - - // HACK(eddyb) Separate encoding a success value (`Ok(output)`) - // from encoding a panic (`Err(e: PanicMessage)`) to avoid - // having handles outside the `bridge.enter(|| ...)` scope, and - // to catch panics that could happen while encoding the success. - // - // Note that panics should be impossible beyond this point, but - // this is defensively trying to avoid any accidental panicking - // reaching the `extern "C"` (which should `abort` but may not - // at the moment, so this is also potentially preventing UB). - b.clear(); - Ok::<_, ()>(output).encode(&mut b, &mut ()); - }) - })) - .map_err(PanicMessage::from) - .unwrap_or_else(|e| { - b.clear(); - Err::<(), _>(e).encode(&mut b, &mut ()); - }); - b -} - impl Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> { pub const fn expand2( f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream ) -> Self { + extern "C" fn run( + bridge: Bridge<'_>, + f: impl FnOnce(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, + ) -> Buffer<u8> { + run_client(bridge, |(input, input2)| { + f(crate::TokenStream(input), crate::TokenStream(input2)).0 + }) + } Client { get_handle_counters: HandleCounters::get, - run: __run_expand2, + run, f, } }
diff --git a/src/libproc_macro/bridge/mod.rs b/src/libproc_macro/bridge/mod.rs index 3c48466..c26b59f 100644 --- a/src/libproc_macro/bridge/mod.rs +++ b/src/libproc_macro/bridge/mod.rs
@@ -148,6 +148,7 @@ fn debug($self: $S::Span) -> String; fn def_site() -> $S::Span; fn call_site() -> $S::Span; + fn mixed_site() -> $S::Span; fn source_file($self: $S::Span) -> $S::SourceFile; fn parent($self: $S::Span) -> Option<$S::Span>; fn source($self: $S::Span) -> $S::Span;
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index d408fef..6166561 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs
@@ -25,8 +25,7 @@ #![feature(extern_types)] #![feature(in_band_lifetimes)] #![feature(optin_builtin_traits)] -#![feature(mem_take)] -#![feature(non_exhaustive)] +#![cfg_attr(bootstrap, feature(non_exhaustive))] #![feature(rustc_attrs)] #![feature(specialization)] @@ -227,7 +226,7 @@ /// To quote `$` itself, use `$$`. #[unstable(feature = "proc_macro_quote", issue = "54722")] #[allow_internal_unstable(proc_macro_def_site)] -#[cfg_attr(not(bootstrap), rustc_builtin_macro)] +#[rustc_builtin_macro] pub macro quote ($($t:tt)*) { /* compiler built-in */ } #[unstable(feature = "proc_macro_internals", issue = "27812")] @@ -271,6 +270,15 @@ Span(bridge::client::Span::call_site()) } + /// A span that represents `macro_rules` hygiene, and sometimes resolves at the macro + /// definition site (local variables, labels, `$crate`) and sometimes at the macro + /// call site (everything else). + /// The span location is taken from the call-site. + #[unstable(feature = "proc_macro_mixed_site", issue = "65049")] + pub fn mixed_site() -> Span { + Span(bridge::client::Span::mixed_site()) + } + /// The original source file into which this span points. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn source_file(&self) -> SourceFile {
diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index 0834faf..de67f46 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml
@@ -10,29 +10,34 @@ doctest = false [dependencies] +# Prevent cc from upgrading all the way to 1.0.46, +# which fails the build (see e.g. #65445.) +cc = "=1.0.37" + arena = { path = "../libarena" } -bitflags = "1.0" +bitflags = "1.2.1" fmt_macros = { path = "../libfmt_macros" } graphviz = { path = "../libgraphviz" } jobserver = "0.1" num_cpus = "1.0" scoped-tls = "1.0" log = { version = "0.4", features = ["release_max_level_info", "std"] } -rustc-rayon = "0.2.0" -rustc-rayon-core = "0.2.0" +rustc-rayon = "0.3.0" +rustc-rayon-core = "0.3.0" polonius-engine = "0.10.0" rustc_apfloat = { path = "../librustc_apfloat" } rustc_target = { path = "../librustc_target" } rustc_macros = { path = "../librustc_macros" } rustc_data_structures = { path = "../librustc_data_structures" } +rustc_index = { path = "../librustc_index" } errors = { path = "../librustc_errors", package = "rustc_errors" } rustc_serialize = { path = "../libserialize", package = "serialize" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } -backtrace = "0.3.3" +backtrace = "0.3.40" parking_lot = "0.9" byteorder = { version = "1.3" } chalk-engine = { version = "0.9.0", default-features=false } rustc_fs_util = { path = "../librustc_fs_util" } -smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } -measureme = "0.3" +smallvec = { version = "0.6.8", features = ["union", "may_dangle"] } +measureme = "0.4"
diff --git a/src/librustc/arena.rs b/src/librustc/arena.rs index d4fc1b1..3daf0fc 100644 --- a/src/librustc/arena.rs +++ b/src/librustc/arena.rs
@@ -26,12 +26,12 @@ [] steal_mir: rustc::ty::steal::Steal<rustc::mir::Body<$tcx>>, [] mir: rustc::mir::Body<$tcx>, [] steal_promoted: rustc::ty::steal::Steal< - rustc_data_structures::indexed_vec::IndexVec< + rustc_index::vec::IndexVec< rustc::mir::Promoted, rustc::mir::Body<$tcx> > >, - [] promoted: rustc_data_structures::indexed_vec::IndexVec< + [] promoted: rustc_index::vec::IndexVec< rustc::mir::Promoted, rustc::mir::Body<$tcx> >, @@ -45,7 +45,7 @@ [decode] specialization_graph: rustc::traits::specialization_graph::Graph, [] region_scope_tree: rustc::middle::region::ScopeTree, [] item_local_set: rustc::util::nodemap::ItemLocalSet, - [decode] mir_const_qualif: rustc_data_structures::bit_set::BitSet<rustc::mir::Local>, + [decode] mir_const_qualif: rustc_index::bit_set::BitSet<rustc::mir::Local>, [] trait_impls_of: rustc::ty::trait_def::TraitImpls, [] dropck_outlives: rustc::infer::canonical::Canonical<'tcx, @@ -86,7 +86,6 @@ rustc::infer::canonical::QueryResponse<'tcx, rustc::ty::Ty<'tcx>> >, [few] crate_inherent_impls: rustc::ty::CrateInherentImpls, - [decode] borrowck: rustc::middle::borrowck::BorrowCheckResult, [few] upstream_monomorphizations: rustc::util::nodemap::DefIdMap< rustc_data_structures::fx::FxHashMap< @@ -99,7 +98,6 @@ rustc::hir::def_id::DefId, >, [few] resolve_lifetimes: rustc::middle::resolve_lifetime::ResolveLifetimes, - [decode] generic_predicates: rustc::ty::GenericPredicates<'tcx>, [few] lint_levels: rustc::lint::LintLevelMap, [few] stability_index: rustc::middle::stability::Index<'tcx>, [few] features: syntax::feature_gate::Features,
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 3d5e7dd..dea8d70 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs
@@ -59,7 +59,7 @@ use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use std::fmt; use std::hash::Hash; -use syntax_pos::symbol::InternedString; +use syntax_pos::symbol::Symbol; use crate::traits; use crate::traits::query::{ CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal, @@ -114,7 +114,6 @@ impl DepKind { #[allow(unreachable_code)] - #[inline] pub fn can_reconstruct_query_key<$tcx>(&self) -> bool { match *self { $( @@ -150,7 +149,6 @@ } } - #[inline(always)] pub fn is_eval_always(&self) -> bool { match *self { $( @@ -199,7 +197,6 @@ impl DepNode { #[allow(unreachable_code, non_snake_case)] - #[inline(always)] pub fn new<'tcx>(tcx: TyCtxt<'tcx>, dep: DepConstructor<'tcx>) -> DepNode @@ -219,14 +216,16 @@ hash }; - if cfg!(debug_assertions) && - !dep_node.kind.can_reconstruct_query_key() && - (tcx.sess.opts.debugging_opts.incremental_info || - tcx.sess.opts.debugging_opts.query_dep_graph) + #[cfg(debug_assertions)] { - tcx.dep_graph.register_dep_node_debug_str(dep_node, || { - arg.to_debug_str(tcx) - }); + if !dep_node.kind.can_reconstruct_query_key() && + (tcx.sess.opts.debugging_opts.incremental_info || + tcx.sess.opts.debugging_opts.query_dep_graph) + { + tcx.dep_graph.register_dep_node_debug_str(dep_node, || { + arg.to_debug_str(tcx) + }); + } } return dep_node; @@ -242,14 +241,16 @@ hash }; - if cfg!(debug_assertions) && - !dep_node.kind.can_reconstruct_query_key() && - (tcx.sess.opts.debugging_opts.incremental_info || - tcx.sess.opts.debugging_opts.query_dep_graph) + #[cfg(debug_assertions)] { - tcx.dep_graph.register_dep_node_debug_str(dep_node, || { - tupled_args.to_debug_str(tcx) - }); + if !dep_node.kind.can_reconstruct_query_key() && + (tcx.sess.opts.debugging_opts.incremental_info || + tcx.sess.opts.debugging_opts.query_dep_graph) + { + tcx.dep_graph.register_dep_node_debug_str(dep_node, || { + tupled_args.to_debug_str(tcx) + }); + } } return dep_node; @@ -267,7 +268,6 @@ /// Construct a DepNode from the given DepKind and DefPathHash. This /// method will assert that the given DepKind actually requires a /// single DefId/DefPathHash parameter. - #[inline(always)] pub fn from_def_path_hash(kind: DepKind, def_path_hash: DefPathHash) -> DepNode { @@ -281,7 +281,6 @@ /// Creates a new, parameterless DepNode. This method will assert /// that the DepNode corresponding to the given DepKind actually /// does not require any parameters. - #[inline(always)] pub fn new_no_params(kind: DepKind) -> DepNode { debug_assert!(!kind.has_params()); DepNode { @@ -300,7 +299,6 @@ /// DepNode. Condition (2) might not be fulfilled if a DepNode /// refers to something from the previous compilation session that /// has been removed. - #[inline] pub fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId> { if self.kind.can_reconstruct_query_key() { let def_path_hash = DefPathHash(self.hash); @@ -386,14 +384,12 @@ impl DefPathHash { - #[inline(always)] pub fn to_dep_node(self, kind: DepKind) -> DepNode { DepNode::from_def_path_hash(kind, self) } } impl DefId { - #[inline(always)] pub fn to_dep_node(self, tcx: TyCtxt<'_>, kind: DepKind) -> DepNode { DepNode::from_def_path_hash(kind, tcx.def_path_hash(self)) } @@ -430,7 +426,7 @@ [anon] TraitSelect, - [] CompileCodegenUnit(InternedString), + [] CompileCodegenUnit(Symbol), [eval_always] Analysis(CrateNum), ]);
diff --git a/src/librustc/dep_graph/dep_tracking_map.rs b/src/librustc/dep_graph/dep_tracking_map.rs deleted file mode 100644 index ee22d0b..0000000 --- a/src/librustc/dep_graph/dep_tracking_map.rs +++ /dev/null
@@ -1,87 +0,0 @@ -use rustc_data_structures::fx::FxHashMap; -use std::cell::RefCell; -use std::hash::Hash; -use std::marker::PhantomData; -use crate::util::common::MemoizationMap; - -use super::{DepKind, DepNodeIndex, DepGraph}; - -/// A DepTrackingMap offers a subset of the `Map` API and ensures that -/// we make calls to `read` and `write` as appropriate. We key the -/// maps with a unique type for brevity. -pub struct DepTrackingMap<M: DepTrackingMapConfig> { - phantom: PhantomData<M>, - graph: DepGraph, - map: FxHashMap<M::Key, (M::Value, DepNodeIndex)>, -} - -pub trait DepTrackingMapConfig { - type Key: Eq + Hash + Clone; - type Value: Clone; - fn to_dep_kind() -> DepKind; -} - -impl<M: DepTrackingMapConfig> DepTrackingMap<M> { - pub fn new(graph: DepGraph) -> DepTrackingMap<M> { - DepTrackingMap { - phantom: PhantomData, - graph, - map: Default::default(), - } - } -} - -impl<M: DepTrackingMapConfig> MemoizationMap for RefCell<DepTrackingMap<M>> { - type Key = M::Key; - type Value = M::Value; - - /// Memoizes an entry in the dep-tracking-map. If the entry is not - /// already present, then `op` will be executed to compute its value. - /// The resulting dependency graph looks like this: - /// - /// [op] -> Map(key) -> CurrentTask - /// - /// Here, `[op]` represents whatever nodes `op` reads in the - /// course of execution; `Map(key)` represents the node for this - /// map, and `CurrentTask` represents the current task when - /// `memoize` is invoked. - /// - /// **Important:** when `op` is invoked, the current task will be - /// switched to `Map(key)`. Therefore, if `op` makes use of any - /// HIR nodes or shared state accessed through its closure - /// environment, it must explicitly register a read of that - /// state. As an example, see `type_of_item` in `collect`, - /// which looks something like this: - /// - /// ``` - /// fn type_of_item(..., item: &hir::Item) -> Ty<'tcx> { - /// let item_def_id = ccx.tcx.hir().local_def_id(it.hir_id); - /// ccx.tcx.item_types.memoized(item_def_id, || { - /// ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // (*) - /// compute_type_of_item(ccx, item) - /// }); - /// } - /// ``` - /// - /// The key is the line marked `(*)`: the closure implicitly - /// accesses the body of the item `item`, so we register a read - /// from `Hir(item_def_id)`. - fn memoize<OP>(&self, key: M::Key, op: OP) -> M::Value - where OP: FnOnce() -> M::Value - { - let graph; - { - let this = self.borrow(); - if let Some(&(ref result, dep_node)) = this.map.get(&key) { - this.graph.read_index(dep_node); - return result.clone(); - } - graph = this.graph.clone(); - } - - let (result, dep_node) = graph.with_anon_task(M::to_dep_kind(), op); - self.borrow_mut().map.insert(key, (result.clone(), dep_node)); - graph.read_index(dep_node); - result - } -}
diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index e76a703..0104507 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs
@@ -1,15 +1,16 @@ use errors::Diagnostic; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_index::vec::{Idx, IndexVec}; use smallvec::SmallVec; -use rustc_data_structures::sync::{Lrc, Lock, AtomicU32, Ordering}; +use rustc_data_structures::sync::{Lrc, Lock, AtomicU32, AtomicU64, Ordering}; +use rustc_data_structures::sharded::{self, Sharded}; +use std::sync::atomic::Ordering::SeqCst; use std::env; use std::hash::Hash; use std::collections::hash_map::Entry; use std::mem; use crate::ty::{self, TyCtxt}; -use crate::util::common::{ProfileQueriesMsg, profq_msg}; use parking_lot::{Mutex, Condvar}; use crate::ich::{StableHashingContext, StableHashingContextProvider, Fingerprint}; @@ -26,15 +27,15 @@ data: Option<Lrc<DepGraphData>>, } -newtype_index! { +rustc_index::newtype_index! { pub struct DepNodeIndex { .. } } impl DepNodeIndex { - const INVALID: DepNodeIndex = DepNodeIndex::MAX; + pub const INVALID: DepNodeIndex = DepNodeIndex::MAX; } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(PartialEq)] pub enum DepNodeColor { Red, Green(DepNodeIndex) @@ -54,7 +55,7 @@ /// tracking. The `current` field is the dependency graph of only the /// current compilation session: We don't merge the previous dep-graph into /// current one anymore. - current: Lock<CurrentDepGraph>, + current: CurrentDepGraph, /// The dep-graph from the previous compilation session. It contains all /// nodes and edges as well as all fingerprints of nodes that have them. @@ -75,9 +76,6 @@ previous_work_products: FxHashMap<WorkProductId, WorkProduct>, dep_node_debug: Lock<FxHashMap<DepNode, String>>, - - // Used for testing, only populated when -Zquery-dep-graph is specified. - loaded_from_cache: Lock<FxHashMap<DepNodeIndex, bool>>, } pub fn hash_result<R>(hcx: &mut StableHashingContext<'_>, result: &R) -> Option<Fingerprint> @@ -99,12 +97,11 @@ data: Some(Lrc::new(DepGraphData { previous_work_products: prev_work_products, dep_node_debug: Default::default(), - current: Lock::new(CurrentDepGraph::new(prev_graph_node_count)), + current: CurrentDepGraph::new(prev_graph_node_count), emitting_diagnostics: Default::default(), emitting_diagnostics_cond_var: Condvar::new(), previous: prev_graph, colors: DepNodeColorMap::new(prev_graph_node_count), - loaded_from_cache: Default::default(), })), } } @@ -122,13 +119,12 @@ } pub fn query(&self) -> DepGraphQuery { - let current_dep_graph = self.data.as_ref().unwrap().current.borrow(); - let nodes: Vec<_> = current_dep_graph.data.iter().map(|n| n.node).collect(); + let data = self.data.as_ref().unwrap().current.data.lock(); + let nodes: Vec<_> = data.iter().map(|n| n.node).collect(); let mut edges = Vec::new(); - for (from, edge_targets) in current_dep_graph.data.iter() - .map(|d| (d.node, &d.edges)) { + for (from, edge_targets) in data.iter().map(|d| (d.node, &d.edges)) { for &edge_target in edge_targets.iter() { - let to = current_dep_graph.data[edge_target].node; + let to = data[edge_target].node; edges.push((from, to)); } } @@ -207,7 +203,7 @@ read_set: Default::default(), }), |data, key, fingerprint, task| { - data.borrow_mut().complete_task(key, task.unwrap(), fingerprint) + data.complete_task(key, task.unwrap(), fingerprint) }, hash_result) } @@ -228,7 +224,7 @@ self.with_task_impl(key, cx, input, true, identity_fn, |_| None, |data, key, fingerprint, _| { - data.borrow_mut().alloc_node(key, SmallVec::new(), fingerprint) + data.alloc_node(key, SmallVec::new(), fingerprint) }, hash_result::<R>) } @@ -241,7 +237,7 @@ no_tcx: bool, task: fn(C, A) -> R, create_task: fn(DepNode) -> Option<TaskDeps>, - finish_task_and_alloc_depnode: fn(&Lock<CurrentDepGraph>, + finish_task_and_alloc_depnode: fn(&CurrentDepGraph, DepNode, Fingerprint, Option<TaskDeps>) -> DepNodeIndex, @@ -260,10 +256,6 @@ // - we can get an idea of the runtime cost. let mut hcx = cx.get_stable_hashing_context(); - if cfg!(debug_assertions) { - profq_msg(hcx.sess(), ProfileQueriesMsg::TaskBegin(key.clone())) - }; - let result = if no_tcx { task(cx, arg) } else { @@ -279,10 +271,6 @@ }) }; - if cfg!(debug_assertions) { - profq_msg(hcx.sess(), ProfileQueriesMsg::TaskEnd) - }; - let current_fingerprint = hash_result(&mut hcx, &result); let dep_node_index = finish_task_and_alloc_depnode( @@ -363,7 +351,6 @@ (r, task_deps.into_inner()) }); let dep_node_index = data.current - .borrow_mut() .complete_anon_task(dep_kind, task_deps); (result, dep_node_index) } else { @@ -387,8 +374,7 @@ self.with_task_impl(key, cx, arg, false, task, |_| None, |data, key, fingerprint, _| { - let mut current = data.borrow_mut(); - current.alloc_node(key, smallvec![], fingerprint) + data.alloc_node(key, smallvec![], fingerprint) }, hash_result) } @@ -396,9 +382,9 @@ #[inline] pub fn read(&self, v: DepNode) { if let Some(ref data) = self.data { - let current = data.current.borrow_mut(); - if let Some(&dep_node_index) = current.node_to_node_index.get(&v) { - std::mem::drop(current); + let map = data.current.node_to_node_index.get_shard_by_value(&v).lock(); + if let Some(dep_node_index) = map.get(&v).copied() { + std::mem::drop(map); data.read_index(dep_node_index); } else { bug!("DepKind {:?} should be pre-allocated but isn't.", v.kind) @@ -419,8 +405,9 @@ .as_ref() .unwrap() .current - .borrow_mut() .node_to_node_index + .get_shard_by_value(dep_node) + .lock() .get(dep_node) .cloned() .unwrap() @@ -429,7 +416,11 @@ #[inline] pub fn dep_node_exists(&self, dep_node: &DepNode) -> bool { if let Some(ref data) = self.data { - data.current.borrow_mut().node_to_node_index.contains_key(dep_node) + data.current + .node_to_node_index + .get_shard_by_value(&dep_node) + .lock() + .contains_key(dep_node) } else { false } @@ -437,8 +428,8 @@ #[inline] pub fn fingerprint_of(&self, dep_node_index: DepNodeIndex) -> Fingerprint { - let current = self.data.as_ref().expect("dep graph enabled").current.borrow_mut(); - current.data[dep_node_index].fingerprint + let data = self.data.as_ref().expect("dep graph enabled").current.data.lock(); + data[dep_node_index].fingerprint } pub fn prev_fingerprint_of(&self, dep_node: &DepNode) -> Option<Fingerprint> { @@ -492,32 +483,29 @@ pub fn edge_deduplication_data(&self) -> Option<(u64, u64)> { if cfg!(debug_assertions) { - let current_dep_graph = self.data.as_ref().unwrap().current.borrow(); + let current_dep_graph = &self.data.as_ref().unwrap().current; - Some((current_dep_graph.total_read_count, - current_dep_graph.total_duplicate_read_count)) + Some((current_dep_graph.total_read_count.load(SeqCst), + current_dep_graph.total_duplicate_read_count.load(SeqCst))) } else { None } } pub fn serialize(&self) -> SerializedDepGraph { - let current_dep_graph = self.data.as_ref().unwrap().current.borrow(); + let data = self.data.as_ref().unwrap().current.data.lock(); let fingerprints: IndexVec<SerializedDepNodeIndex, _> = - current_dep_graph.data.iter().map(|d| d.fingerprint).collect(); + data.iter().map(|d| d.fingerprint).collect(); let nodes: IndexVec<SerializedDepNodeIndex, _> = - current_dep_graph.data.iter().map(|d| d.node).collect(); + data.iter().map(|d| d.node).collect(); - let total_edge_count: usize = current_dep_graph.data.iter() - .map(|d| d.edges.len()) - .sum(); + let total_edge_count: usize = data.iter().map(|d| d.edges.len()).sum(); let mut edge_list_indices = IndexVec::with_capacity(nodes.len()); let mut edge_list_data = Vec::with_capacity(total_edge_count); - for (current_dep_node_index, edges) in current_dep_graph.data.iter_enumerated() - .map(|(i, d)| (i, &d.edges)) { + for (current_dep_node_index, edges) in data.iter_enumerated().map(|(i, d)| (i, &d.edges)) { let start = edge_list_data.len() as u32; // This should really just be a memcpy :/ edge_list_data.extend(edges.iter().map(|i| SerializedDepNodeIndex::new(i.index()))); @@ -590,7 +578,7 @@ // mark it as green by recursively marking all of its // dependencies green. self.try_mark_previous_green( - tcx.global_tcx(), + tcx, data, prev_index, &dep_node @@ -613,7 +601,11 @@ #[cfg(not(parallel_compiler))] { - debug_assert!(!data.current.borrow().node_to_node_index.contains_key(dep_node)); + debug_assert!(!data.current + .node_to_node_index + .get_shard_by_value(dep_node) + .lock() + .contains_key(dep_node)); debug_assert!(data.colors.get(prev_dep_node_index).is_none()); } @@ -746,15 +738,13 @@ // There may be multiple threads trying to mark the same dep node green concurrently let dep_node_index = { - let mut current = data.current.borrow_mut(); - // Copy the fingerprint from the previous graph, // so we don't have to recompute it let fingerprint = data.previous.fingerprint_by_index(prev_dep_node_index); // We allocating an entry for the node in the current dependency graph and // adding all the appropriate edges imported from the previous graph - current.intern_node(*dep_node, current_deps, fingerprint) + data.current.intern_node(*dep_node, current_deps, fingerprint) }; // ... emitting any stored diagnostic ... @@ -858,6 +848,8 @@ // This method will only load queries that will end up in the disk cache. // Other queries will not be executed. pub fn exec_cache_promotions(&self, tcx: TyCtxt<'_>) { + let _prof_timer = tcx.prof.generic_activity("incr_comp_query_cache_promotion"); + let data = self.data.as_ref().unwrap(); for prev_index in data.colors.values.indices() { match data.colors.get(prev_index) { @@ -874,25 +866,6 @@ } } } - - pub fn mark_loaded_from_cache(&self, dep_node_index: DepNodeIndex, state: bool) { - debug!("mark_loaded_from_cache({:?}, {})", - self.data.as_ref().unwrap().current.borrow().data[dep_node_index].node, - state); - - self.data - .as_ref() - .unwrap() - .loaded_from_cache - .borrow_mut() - .insert(dep_node_index, state); - } - - pub fn was_loaded_from_cache(&self, dep_node: &DepNode) -> Option<bool> { - let data = self.data.as_ref().unwrap(); - let dep_node_index = data.current.borrow().node_to_node_index[dep_node]; - data.loaded_from_cache.borrow().get(&dep_node_index).cloned() - } } /// A "work product" is an intermediate result that we save into the @@ -947,9 +920,27 @@ fingerprint: Fingerprint, } +/// `CurrentDepGraph` stores the dependency graph for the current session. +/// It will be populated as we run queries or tasks. +/// +/// The nodes in it are identified by an index (`DepNodeIndex`). +/// The data for each node is stored in its `DepNodeData`, found in the `data` field. +/// +/// We never remove nodes from the graph: they are only added. +/// +/// This struct uses two locks internally. The `data` and `node_to_node_index` fields are +/// locked separately. Operations that take a `DepNodeIndex` typically just access +/// the data field. +/// +/// The only operation that must manipulate both locks is adding new nodes, in which case +/// we first acquire the `node_to_node_index` lock and then, once a new node is to be inserted, +/// acquire the lock on `data.` pub(super) struct CurrentDepGraph { - data: IndexVec<DepNodeIndex, DepNodeData>, - node_to_node_index: FxHashMap<DepNode, DepNodeIndex>, + data: Lock<IndexVec<DepNodeIndex, DepNodeData>>, + node_to_node_index: Sharded<FxHashMap<DepNode, DepNodeIndex>>, + + /// Used to trap when a specific edge is added to the graph. + /// This is used for debug purposes and is only active with `debug_assertions`. #[allow(dead_code)] forbidden_edge: Option<EdgeFilter>, @@ -966,8 +957,10 @@ /// the `DepGraph` is created. anon_id_seed: Fingerprint, - total_read_count: u64, - total_duplicate_read_count: u64, + /// These are simple counters that are for profiling and + /// debugging and only active with `debug_assertions`. + total_read_count: AtomicU64, + total_duplicate_read_count: AtomicU64, } impl CurrentDepGraph { @@ -1001,20 +994,20 @@ let new_node_count_estimate = (prev_graph_node_count * 102) / 100 + 200; CurrentDepGraph { - data: IndexVec::with_capacity(new_node_count_estimate), - node_to_node_index: FxHashMap::with_capacity_and_hasher( - new_node_count_estimate, + data: Lock::new(IndexVec::with_capacity(new_node_count_estimate)), + node_to_node_index: Sharded::new(|| FxHashMap::with_capacity_and_hasher( + new_node_count_estimate / sharded::SHARDS, Default::default(), - ), + )), anon_id_seed: stable_hasher.finish(), forbidden_edge, - total_read_count: 0, - total_duplicate_read_count: 0, + total_read_count: AtomicU64::new(0), + total_duplicate_read_count: AtomicU64::new(0), } } fn complete_task( - &mut self, + &self, node: DepNode, task_deps: TaskDeps, fingerprint: Fingerprint @@ -1022,7 +1015,7 @@ self.alloc_node(node, task_deps.reads, fingerprint) } - fn complete_anon_task(&mut self, kind: DepKind, task_deps: TaskDeps) -> DepNodeIndex { + fn complete_anon_task(&self, kind: DepKind, task_deps: TaskDeps) -> DepNodeIndex { debug_assert!(!kind.is_eval_always()); let mut hasher = StableHasher::new(); @@ -1047,28 +1040,30 @@ } fn alloc_node( - &mut self, + &self, dep_node: DepNode, edges: SmallVec<[DepNodeIndex; 8]>, fingerprint: Fingerprint ) -> DepNodeIndex { - debug_assert!(!self.node_to_node_index.contains_key(&dep_node)); + debug_assert!(!self.node_to_node_index + .get_shard_by_value(&dep_node) + .lock() + .contains_key(&dep_node)); self.intern_node(dep_node, edges, fingerprint) } fn intern_node( - &mut self, + &self, dep_node: DepNode, edges: SmallVec<[DepNodeIndex; 8]>, fingerprint: Fingerprint ) -> DepNodeIndex { - debug_assert_eq!(self.node_to_node_index.len(), self.data.len()); - - match self.node_to_node_index.entry(dep_node) { + match self.node_to_node_index.get_shard_by_value(&dep_node).lock().entry(dep_node) { Entry::Occupied(entry) => *entry.get(), Entry::Vacant(entry) => { - let dep_node_index = DepNodeIndex::new(self.data.len()); - self.data.push(DepNodeData { + let mut data = self.data.lock(); + let dep_node_index = DepNodeIndex::new(data.len()); + data.push(DepNodeData { node: dep_node, edges, fingerprint @@ -1087,7 +1082,7 @@ if let Some(task_deps) = icx.task_deps { let mut task_deps = task_deps.lock(); if cfg!(debug_assertions) { - self.current.lock().total_read_count += 1; + self.current.total_read_count.fetch_add(1, SeqCst); } if task_deps.read_set.insert(source) { task_deps.reads.push(source); @@ -1095,9 +1090,9 @@ #[cfg(debug_assertions)] { if let Some(target) = task_deps.node { - let graph = self.current.lock(); - if let Some(ref forbidden_edge) = graph.forbidden_edge { - let source = graph.data[source].node; + let data = self.current.data.lock(); + if let Some(ref forbidden_edge) = self.current.forbidden_edge { + let source = data[source].node; if forbidden_edge.test(&source, &target) { bug!("forbidden edge {:?} -> {:?} created", source, @@ -1107,7 +1102,7 @@ } } } else if cfg!(debug_assertions) { - self.current.lock().total_duplicate_read_count += 1; + self.current.total_duplicate_read_count.fetch_add(1, SeqCst); } } })
diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs index 1535e6d..43f3d7e 100644 --- a/src/librustc/dep_graph/mod.rs +++ b/src/librustc/dep_graph/mod.rs
@@ -1,6 +1,5 @@ pub mod debug; mod dep_node; -mod dep_tracking_map; mod graph; mod prev; mod query; @@ -8,7 +7,6 @@ mod serialized; pub mod cgu_reuse_tracker; -pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig}; pub use self::dep_node::{DepNode, DepKind, DepConstructor, WorkProductId, RecoverKey, label_strs}; pub use self::graph::{DepGraph, WorkProduct, DepNodeIndex, DepNodeColor, TaskDeps, hash_result}; pub use self::graph::WorkProductFileKind;
diff --git a/src/librustc/dep_graph/serialized.rs b/src/librustc/dep_graph/serialized.rs index b64f71e..4302195 100644 --- a/src/librustc/dep_graph/serialized.rs +++ b/src/librustc/dep_graph/serialized.rs
@@ -2,9 +2,9 @@ use crate::dep_graph::DepNode; use crate::ich::Fingerprint; -use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use rustc_index::vec::{IndexVec, Idx}; -newtype_index! { +rustc_index::newtype_index! { pub struct SerializedDepNodeIndex { .. } }
diff --git a/src/librustc/error_codes.rs b/src/librustc/error_codes.rs index f6564f1..3e35add 100644 --- a/src/librustc/error_codes.rs +++ b/src/librustc/error_codes.rs
@@ -259,8 +259,8 @@ This is similar to the second sub-error, but subtler. It happens in situations like the following: -```compile_fail -trait Super<A> {} +```compile_fail,E0038 +trait Super<A: ?Sized> {} trait Trait: Super<Self> { } @@ -270,17 +270,21 @@ impl Super<Foo> for Foo{} impl Trait for Foo {} + +fn main() { + let x: Box<dyn Trait>; +} ``` Here, the supertrait might have methods as follows: ``` -trait Super<A> { - fn get_a(&self) -> A; // note that this is object safe! +trait Super<A: ?Sized> { + fn get_a(&self) -> &A; // note that this is object safe! } ``` -If the trait `Foo` was deriving from something like `Super<String>` or +If the trait `Trait` was deriving from something like `Super<String>` or `Super<T>` (where `Foo` itself is `Foo<T>`), this is okay, because given a type `get_a()` will definitely return an object of that type. @@ -466,67 +470,6 @@ ``` "##, -// This shouldn't really ever trigger since the repeated value error comes first -E0136: r##" -A binary can only have one entry point, and by default that entry point is the -function `main()`. If there are multiple such functions, please rename one. -"##, - -E0137: r##" -More than one function was declared with the `#[main]` attribute. - -Erroneous code example: - -```compile_fail,E0137 -#![feature(main)] - -#[main] -fn foo() {} - -#[main] -fn f() {} // error: multiple functions with a `#[main]` attribute -``` - -This error indicates that the compiler found multiple functions with the -`#[main]` attribute. This is an error because there must be a unique entry -point into a Rust program. Example: - -``` -#![feature(main)] - -#[main] -fn f() {} // ok! -``` -"##, - -E0138: r##" -More than one function was declared with the `#[start]` attribute. - -Erroneous code example: - -```compile_fail,E0138 -#![feature(start)] - -#[start] -fn foo(argc: isize, argv: *const *const u8) -> isize {} - -#[start] -fn f(argc: isize, argv: *const *const u8) -> isize {} -// error: multiple 'start' functions -``` - -This error indicates that the compiler found multiple functions with the -`#[start]` attribute. This is an error because there must be a unique entry -point into a Rust program. Example: - -``` -#![feature(start)] - -#[start] -fn foo(argc: isize, argv: *const *const u8) -> isize { 0 } // ok! -``` -"##, - E0139: r##" #### Note: this error code is no longer emitted by the compiler. @@ -1580,8 +1523,51 @@ ``` "##, +E0495: r##" +A lifetime cannot be determined in the given situation. + +Erroneous code example: + +```compile_fail,E0495 +fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T { + match (&t,) { // error! + ((u,),) => u, + } +} + +let y = Box::new((42,)); +let x = transmute_lifetime(&y); +``` + +In this code, you have two ways to solve this issue: + 1. Enforce that `'a` lives at least as long as `'b`. + 2. Use the same lifetime requirement for both input and output values. + +So for the first solution, you can do it by replacing `'a` with `'a: 'b`: + +``` +fn transmute_lifetime<'a: 'b, 'b, T>(t: &'a (T,)) -> &'b T { + match (&t,) { // ok! + ((u,),) => u, + } +} +``` + +In the second you can do it by simply removing `'b` so they both use `'a`: + +``` +fn transmute_lifetime<'a, T>(t: &'a (T,)) -> &'a T { + match (&t,) { // ok! + ((u,),) => u, + } +} +``` +"##, + E0496: r##" -A lifetime name is shadowing another lifetime name. Erroneous code example: +A lifetime name is shadowing another lifetime name. + +Erroneous code example: ```compile_fail,E0496 struct Foo<'a> { @@ -1613,8 +1599,11 @@ "##, E0497: r##" -A stability attribute was used outside of the standard library. Erroneous code -example: +#### Note: this error code is no longer emitted by the compiler. + +A stability attribute was used outside of the standard library. + +Erroneous code example: ```compile_fail #[stable] // error: stability attributes may not be used outside of the @@ -1626,33 +1615,6 @@ Also, for now, it is not possible to write deprecation messages either. "##, -E0512: r##" -Transmute with two differently sized types was attempted. Erroneous code -example: - -```compile_fail,E0512 -fn takes_u8(_: u8) {} - -fn main() { - unsafe { takes_u8(::std::mem::transmute(0u16)); } - // error: cannot transmute between types of different sizes, - // or dependently-sized types -} -``` - -Please use types with same size or use the expected type directly. Example: - -``` -fn takes_u8(_: u8) {} - -fn main() { - unsafe { takes_u8(::std::mem::transmute(0i8)); } // ok! - // or: - unsafe { takes_u8(0u8); } // ok! -} -``` -"##, - E0517: r##" This error indicates that a `#[repr(..)]` attribute was placed on an unsupported item. @@ -1787,6 +1749,27 @@ https://doc.rust-lang.org/book/ch13-01-closures.html "##, +E0566: r##" +Conflicting representation hints have been used on a same item. + +Erroneous code example: + +``` +#[repr(u32, u64)] // warning! +enum Repr { A } +``` + +In most cases (if not all), using just one representation hint is more than +enough. If you want to have a representation hint depending on the current +architecture, use `cfg_attr`. Example: + +``` +#[cfg_attr(linux, repr(u32))] +#[cfg_attr(not(linux), repr(u64))] +enum Repr { A } +``` +"##, + E0580: r##" The `main` function was incorrectly declared. @@ -1847,84 +1830,6 @@ [RFC 1522]: https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md "##, -E0591: r##" -Per [RFC 401][rfc401], if you have a function declaration `foo`: - -``` -// For the purposes of this explanation, all of these -// different kinds of `fn` declarations are equivalent: -struct S; -fn foo(x: S) { /* ... */ } -# #[cfg(for_demonstration_only)] -extern "C" { fn foo(x: S); } -# #[cfg(for_demonstration_only)] -impl S { fn foo(self) { /* ... */ } } -``` - -the type of `foo` is **not** `fn(S)`, as one might expect. -Rather, it is a unique, zero-sized marker type written here as `typeof(foo)`. -However, `typeof(foo)` can be _coerced_ to a function pointer `fn(S)`, -so you rarely notice this: - -``` -# struct S; -# fn foo(_: S) {} -let x: fn(S) = foo; // OK, coerces -``` - -The reason that this matter is that the type `fn(S)` is not specific to -any particular function: it's a function _pointer_. So calling `x()` results -in a virtual call, whereas `foo()` is statically dispatched, because the type -of `foo` tells us precisely what function is being called. - -As noted above, coercions mean that most code doesn't have to be -concerned with this distinction. However, you can tell the difference -when using **transmute** to convert a fn item into a fn pointer. - -This is sometimes done as part of an FFI: - -```compile_fail,E0591 -extern "C" fn foo(userdata: Box<i32>) { - /* ... */ -} - -# fn callback(_: extern "C" fn(*mut i32)) {} -# use std::mem::transmute; -# unsafe { -let f: extern "C" fn(*mut i32) = transmute(foo); -callback(f); -# } -``` - -Here, transmute is being used to convert the types of the fn arguments. -This pattern is incorrect because, because the type of `foo` is a function -**item** (`typeof(foo)`), which is zero-sized, and the target type (`fn()`) -is a function pointer, which is not zero-sized. -This pattern should be rewritten. There are a few possible ways to do this: - -- change the original fn declaration to match the expected signature, - and do the cast in the fn body (the preferred option) -- cast the fn item fo a fn pointer before calling transmute, as shown here: - - ``` - # extern "C" fn foo(_: Box<i32>) {} - # use std::mem::transmute; - # unsafe { - let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_)); - let f: extern "C" fn(*mut i32) = transmute(foo as usize); // works too - # } - ``` - -The same applies to transmutes to `*mut fn()`, which were observed in practice. -Note though that use of this type is generally incorrect. -The intention is typically to describe a function pointer, but just `fn()` -alone suffices for that. `*mut fn()` is a pointer to a fn pointer. -(Since these values are typically just passed to C code, however, this rarely -makes a difference in practice.) - -[rfc401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md -"##, - E0593: r##" You tried to supply an `Fn`-based type with an incorrect number of arguments than what was expected. @@ -1941,21 +1846,6 @@ ``` "##, -E0601: r##" -No `main` function was found in a binary crate. To fix this error, add a -`main` function. For example: - -``` -fn main() { - // Your program will start here. - println!("Hello world!"); -} -``` - -If you don't know the basics of Rust, you can go look to the Rust Book to get -started: https://doc.rust-lang.org/book/ -"##, - E0602: r##" An unknown lint was used on the command line. @@ -2115,6 +2005,24 @@ transparent wrapper around a float. This can make a difference for the ABI. "##, +E0697: r##" +A closure has been used as `static`. + +Erroneous code example: + +```compile_fail,E0697 +fn main() { + static || {}; // used as `static` +} +``` + +Closures cannot be used as `static`. They "save" the environment, +and as such a static closure would save only a static environment +which would consist only of variables with a static lifetime. Given +this it would be better to use a proper function. The easiest fix +is to remove the `static` keyword. +"##, + E0698: r##" When using generators (or async) all type variables must be bound so a generator can be constructed. @@ -2137,8 +2045,8 @@ async fn bar<T>() -> () {} async fn foo() { - bar::<String>().await; - // ^^^^^^^^ specify type explicitly + bar::<String>().await; + // ^^^^^^^^ specify type explicitly } ``` "##, @@ -2197,8 +2105,6 @@ Examples of erroneous code: ```compile_fail,E0701 -# #![feature(non_exhaustive)] - #[non_exhaustive] trait Foo { } ``` @@ -2217,6 +2123,171 @@ static X: u32 = 42; ``` "##, + +E0728: r##" +[`await`] has been used outside [`async`] function or block. + +Erroneous code examples: + +```edition2018,compile_fail,E0728 +# use std::pin::Pin; +# use std::future::Future; +# use std::task::{Context, Poll}; +# +# struct WakeOnceThenComplete(bool); +# +# fn wake_and_yield_once() -> WakeOnceThenComplete { +# WakeOnceThenComplete(false) +# } +# +# impl Future for WakeOnceThenComplete { +# type Output = (); +# fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { +# if self.0 { +# Poll::Ready(()) +# } else { +# cx.waker().wake_by_ref(); +# self.0 = true; +# Poll::Pending +# } +# } +# } +# +fn foo() { + wake_and_yield_once().await // `await` is used outside `async` context +} +``` + +[`await`] is used to suspend the current computation until the given +future is ready to produce a value. So it is legal only within +an [`async`] context, like an `async fn` or an `async` block. + +```edition2018 +# use std::pin::Pin; +# use std::future::Future; +# use std::task::{Context, Poll}; +# +# struct WakeOnceThenComplete(bool); +# +# fn wake_and_yield_once() -> WakeOnceThenComplete { +# WakeOnceThenComplete(false) +# } +# +# impl Future for WakeOnceThenComplete { +# type Output = (); +# fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { +# if self.0 { +# Poll::Ready(()) +# } else { +# cx.waker().wake_by_ref(); +# self.0 = true; +# Poll::Pending +# } +# } +# } +# +async fn foo() { + wake_and_yield_once().await // `await` is used within `async` function +} + +fn bar(x: u8) -> impl Future<Output = u8> { + async move { + wake_and_yield_once().await; // `await` is used within `async` block + x + } +} +``` + +[`async`]: https://doc.rust-lang.org/std/keyword.async.html +[`await`]: https://doc.rust-lang.org/std/keyword.await.html +"##, + +E0734: r##" +A stability attribute has been used outside of the standard library. + +Erroneous code examples: + +```compile_fail,E0734 +#[rustc_deprecated(since = "b", reason = "text")] // invalid +#[stable(feature = "a", since = "b")] // invalid +#[unstable(feature = "b", issue = "0")] // invalid +fn foo(){} +``` + +These attributes are meant to only be used by the standard library and are +rejected in your own crates. +"##, + +E0736: r##" +`#[track_caller]` and `#[naked]` cannot both be applied to the same function. + +Erroneous code example: + +```compile_fail,E0736 +#![feature(track_caller)] + +#[naked] +#[track_caller] +fn foo() {} +``` + +This is primarily due to ABI incompatibilities between the two attributes. +See [RFC 2091] for details on this and other limitations. + +[RFC 2091]: https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md +"##, + +E0738: r##" +`#[track_caller]` cannot be used in traits yet. This is due to limitations in +the compiler which are likely to be temporary. See [RFC 2091] for details on +this and other restrictions. + +Erroneous example with a trait method implementation: + +```compile_fail,E0738 +#![feature(track_caller)] + +trait Foo { + fn bar(&self); +} + +impl Foo for u64 { + #[track_caller] + fn bar(&self) {} +} +``` + +Erroneous example with a blanket trait method implementation: + +```compile_fail,E0738 +#![feature(track_caller)] + +trait Foo { + #[track_caller] + fn bar(&self) {} + fn baz(&self); +} +``` + +Erroneous example with a trait method declaration: + +```compile_fail,E0738 +#![feature(track_caller)] + +trait Foo { + fn bar(&self) {} + + #[track_caller] + fn baz(&self); +} +``` + +Note that while the compiler may be able to support the attribute in traits in +the future, [RFC 2091] prohibits their implementation without a follow-up RFC. + +[RFC 2091]: https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md +"##, + ; // E0006, // merged with E0005 // E0101, // replaced with E0282 @@ -2226,7 +2297,7 @@ // E0272, // on_unimplemented #0 // E0273, // on_unimplemented #1 // E0274, // on_unimplemented #2 - E0278, // requirement is not satisfied +// E0278, // requirement is not satisfied E0279, // requirement is not satisfied E0280, // requirement is not satisfied // E0285, // overflow evaluation builtin bounds @@ -2258,9 +2329,6 @@ E0488, // lifetime of variable does not enclose its declaration E0489, // type/lifetime parameter not in scope here E0490, // a value of type `..` is borrowed for too long - E0495, // cannot infer an appropriate lifetime due to conflicting - // requirements - E0566, // conflicting representation hints E0623, // lifetime mismatch where both parameters are anonymous regions E0628, // generators cannot have explicit parameters E0631, // type mismatch in closure arguments @@ -2268,15 +2336,14 @@ E0657, // `impl Trait` can only capture lifetimes bound at the fn level E0687, // in-band lifetimes cannot be used in `fn`/`Fn` syntax E0688, // in-band lifetimes cannot be mixed with explicit lifetime binders - E0697, // closures cannot be static - E0707, // multiple elided lifetimes used in arguments of `async fn` +// E0707, // multiple elided lifetimes used in arguments of `async fn` E0708, // `async` non-`move` closures with parameters are not currently // supported - E0709, // multiple different lifetimes used in arguments of `async fn` +// E0709, // multiple different lifetimes used in arguments of `async fn` E0710, // an unknown tool name found in scoped lint E0711, // a feature has been declared with conflicting stability attributes // E0702, // replaced with a generic attribute input check E0726, // non-explicit (not `'_`) elided lifetime in unsupported position E0727, // `async` generators are not yet supported - E0728, // `await` must be in an `async` function or block + E0739, // invalid track_caller application/syntax }
diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs index 1df0942..9656200 100644 --- a/src/librustc/hir/check_attr.rs +++ b/src/librustc/hir/check_attr.rs
@@ -4,17 +4,25 @@ //! conflicts between multiple such attributes attached to the same //! item. -use crate::hir; +use crate::hir::{self, HirId, HirVec, Attribute, Item, ItemKind, TraitItem, TraitItemKind}; +use crate::hir::DUMMY_HIR_ID; use crate::hir::def_id::DefId; use crate::hir::intravisit::{self, Visitor, NestedVisitorMap}; +use crate::lint::builtin::UNUSED_ATTRIBUTES; use crate::ty::TyCtxt; use crate::ty::query::Providers; use std::fmt::{self, Display}; -use syntax::symbol::sym; +use syntax::{attr, symbol::sym}; use syntax_pos::Span; #[derive(Copy, Clone, PartialEq)] +pub(crate) enum MethodKind { + Trait { body: bool }, + Inherent, +} + +#[derive(Copy, Clone, PartialEq)] pub(crate) enum Target { ExternCrate, Use, @@ -35,6 +43,12 @@ Impl, Expression, Statement, + AssocConst, + Method(MethodKind), + AssocTy, + ForeignFn, + ForeignStatic, + ForeignTy, } impl Display for Target { @@ -59,29 +73,76 @@ Target::Impl => "item", Target::Expression => "expression", Target::Statement => "statement", + Target::AssocConst => "associated const", + Target::Method(_) => "method", + Target::AssocTy => "associated type", + Target::ForeignFn => "foreign function", + Target::ForeignStatic => "foreign static item", + Target::ForeignTy => "foreign type", }) } } impl Target { - pub(crate) fn from_item(item: &hir::Item) -> Target { - match item.node { - hir::ItemKind::ExternCrate(..) => Target::ExternCrate, - hir::ItemKind::Use(..) => Target::Use, - hir::ItemKind::Static(..) => Target::Static, - hir::ItemKind::Const(..) => Target::Const, - hir::ItemKind::Fn(..) => Target::Fn, - hir::ItemKind::Mod(..) => Target::Mod, - hir::ItemKind::ForeignMod(..) => Target::ForeignMod, - hir::ItemKind::GlobalAsm(..) => Target::GlobalAsm, - hir::ItemKind::TyAlias(..) => Target::TyAlias, - hir::ItemKind::OpaqueTy(..) => Target::OpaqueTy, - hir::ItemKind::Enum(..) => Target::Enum, - hir::ItemKind::Struct(..) => Target::Struct, - hir::ItemKind::Union(..) => Target::Union, - hir::ItemKind::Trait(..) => Target::Trait, - hir::ItemKind::TraitAlias(..) => Target::TraitAlias, - hir::ItemKind::Impl(..) => Target::Impl, + pub(crate) fn from_item(item: &Item) -> Target { + match item.kind { + ItemKind::ExternCrate(..) => Target::ExternCrate, + ItemKind::Use(..) => Target::Use, + ItemKind::Static(..) => Target::Static, + ItemKind::Const(..) => Target::Const, + ItemKind::Fn(..) => Target::Fn, + ItemKind::Mod(..) => Target::Mod, + ItemKind::ForeignMod(..) => Target::ForeignMod, + ItemKind::GlobalAsm(..) => Target::GlobalAsm, + ItemKind::TyAlias(..) => Target::TyAlias, + ItemKind::OpaqueTy(..) => Target::OpaqueTy, + ItemKind::Enum(..) => Target::Enum, + ItemKind::Struct(..) => Target::Struct, + ItemKind::Union(..) => Target::Union, + ItemKind::Trait(..) => Target::Trait, + ItemKind::TraitAlias(..) => Target::TraitAlias, + ItemKind::Impl(..) => Target::Impl, + } + } + + fn from_trait_item(trait_item: &TraitItem) -> Target { + match trait_item.kind { + TraitItemKind::Const(..) => Target::AssocConst, + TraitItemKind::Method(_, hir::TraitMethod::Required(_)) => { + Target::Method(MethodKind::Trait { body: false }) + } + TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => { + Target::Method(MethodKind::Trait { body: true }) + } + TraitItemKind::Type(..) => Target::AssocTy, + } + } + + fn from_foreign_item(foreign_item: &hir::ForeignItem) -> Target { + match foreign_item.kind { + hir::ForeignItemKind::Fn(..) => Target::ForeignFn, + hir::ForeignItemKind::Static(..) => Target::ForeignStatic, + hir::ForeignItemKind::Type => Target::ForeignTy, + } + } + + fn from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem) -> Target { + match impl_item.kind { + hir::ImplItemKind::Const(..) => Target::AssocConst, + hir::ImplItemKind::Method(..) => { + let parent_hir_id = tcx.hir().get_parent_item(impl_item.hir_id); + let containing_item = tcx.hir().expect_item(parent_hir_id); + let containing_impl_is_for_trait = match &containing_item.kind { + hir::ItemKind::Impl(_, _, _, _, tr, _, _) => tr.is_some(), + _ => bug!("parent of an ImplItem must be an Impl"), + }; + if containing_impl_is_for_trait { + Target::Method(MethodKind::Trait { body: true }) + } else { + Target::Method(MethodKind::Inherent) + } + } + hir::ImplItemKind::TyAlias(..) | hir::ImplItemKind::OpaqueTy(..) => Target::AssocTy, } } } @@ -92,79 +153,194 @@ impl CheckAttrVisitor<'tcx> { /// Checks any attribute. - fn check_attributes(&self, item: &hir::Item, target: Target) { - if target == Target::Fn || target == Target::Const { - self.tcx.codegen_fn_attrs(self.tcx.hir().local_def_id(item.hir_id)); - } else if let Some(a) = item.attrs.iter().find(|a| a.check_name(sym::target_feature)) { - self.tcx.sess.struct_span_err(a.span, "attribute should be applied to a function") - .span_label(item.span, "not a function") - .emit(); + fn check_attributes( + &self, + hir_id: HirId, + attrs: &HirVec<Attribute>, + span: &Span, + target: Target, + item: Option<&Item>, + ) { + let mut is_valid = true; + for attr in attrs { + is_valid &= if attr.check_name(sym::inline) { + self.check_inline(hir_id, attr, span, target) + } else if attr.check_name(sym::non_exhaustive) { + self.check_non_exhaustive(attr, span, target) + } else if attr.check_name(sym::marker) { + self.check_marker(attr, span, target) + } else if attr.check_name(sym::target_feature) { + self.check_target_feature(attr, span, target) + } else if attr.check_name(sym::track_caller) { + self.check_track_caller(&attr.span, attrs, span, target) + } else { + true + }; } - for attr in &item.attrs { - if attr.check_name(sym::inline) { - self.check_inline(attr, &item.span, target) - } else if attr.check_name(sym::non_exhaustive) { - self.check_non_exhaustive(attr, item, target) - } else if attr.check_name(sym::marker) { - self.check_marker(attr, item, target) + if !is_valid { + return; + } + + if target == Target::Fn { + self.tcx.codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id)); + } + + self.check_repr(attrs, span, target, item); + self.check_used(attrs, target); + } + + /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. + fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool { + match target { + Target::Fn | Target::Closure | Target::Method(MethodKind::Trait { body: true }) + | Target::Method(MethodKind::Inherent) => true, + Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { + self.tcx.struct_span_lint_hir( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + "`#[inline]` is ignored on function prototypes", + ).emit(); + true + } + // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with + // just a lint, because we previously erroneously allowed it and some crates used it + // accidentally, to to be compatible with crates depending on them, we can't throw an + // error here. + Target::AssocConst => { + self.tcx.struct_span_lint_hir( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + "`#[inline]` is ignored on constants", + ).warn("this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!") + .note("for more information, see issue #65833 \ + <https://github.com/rust-lang/rust/issues/65833>") + .emit(); + true + } + _ => { + struct_span_err!( + self.tcx.sess, + attr.span, + E0518, + "attribute should be applied to function or closure", + ).span_label(*span, "not a function or closure") + .emit(); + false } } - - self.check_repr(item, target); - self.check_used(item, target); } - /// Checks if an `#[inline]` is applied to a function or a closure. - fn check_inline(&self, attr: &hir::Attribute, span: &Span, target: Target) { - if target != Target::Fn && target != Target::Closure { - struct_span_err!(self.tcx.sess, - attr.span, - E0518, - "attribute should be applied to function or closure") - .span_label(*span, "not a function or closure") + /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid. + fn check_track_caller( + &self, + attr_span: &Span, + attrs: &HirVec<Attribute>, + span: &Span, + target: Target, + ) -> bool { + match target { + Target::Fn if attr::contains_name(attrs, sym::naked) => { + struct_span_err!( + self.tcx.sess, + *attr_span, + E0736, + "cannot use `#[track_caller]` with `#[naked]`", + ).emit(); + false + } + Target::Fn | Target::Method(MethodKind::Inherent) => true, + Target::Method(_) => { + struct_span_err!( + self.tcx.sess, + *attr_span, + E0738, + "`#[track_caller]` may not be used on trait methods", + ).emit(); + false + } + _ => { + struct_span_err!( + self.tcx.sess, + *attr_span, + E0739, + "attribute should be applied to function" + ) + .span_label(*span, "not a function") .emit(); + false + } } } - /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. - fn check_non_exhaustive(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) { + /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid. + fn check_non_exhaustive( + &self, + attr: &Attribute, + span: &Span, + target: Target, + ) -> bool { match target { - Target::Struct | Target::Enum => { /* Valid */ }, + Target::Struct | Target::Enum => true, _ => { struct_span_err!(self.tcx.sess, attr.span, E0701, "attribute can only be applied to a struct or enum") - .span_label(item.span, "not a struct or enum") + .span_label(*span, "not a struct or enum") .emit(); - return; + false } } } - /// Checks if the `#[marker]` attribute on an `item` is valid. - fn check_marker(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) { + /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid. + fn check_marker(&self, attr: &Attribute, span: &Span, target: Target) -> bool { match target { - Target::Trait => { /* Valid */ }, + Target::Trait => true, _ => { self.tcx.sess .struct_span_err(attr.span, "attribute can only be applied to a trait") - .span_label(item.span, "not a trait") + .span_label(*span, "not a trait") .emit(); - return; + false } } } + /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid. + fn check_target_feature(&self, attr: &Attribute, span: &Span, target: Target) -> bool { + match target { + Target::Fn | Target::Method(MethodKind::Trait { body: true }) + | Target::Method(MethodKind::Inherent) => true, + _ => { + self.tcx.sess + .struct_span_err(attr.span, "attribute should be applied to a function") + .span_label(*span, "not a function") + .emit(); + false + }, + } + } + /// Checks if the `#[repr]` attributes on `item` are valid. - fn check_repr(&self, item: &hir::Item, target: Target) { + fn check_repr( + &self, + attrs: &HirVec<Attribute>, + span: &Span, + target: Target, + item: Option<&Item>, + ) { // Extract the names of all repr hints, e.g., [foo, bar, align] for: // ``` // #[repr(foo)] // #[repr(bar, align(8))] // ``` - let hints: Vec<_> = item.attrs + let hints: Vec<_> = attrs .iter() .filter(|attr| attr.check_name(sym::repr)) .filter_map(|attr| attr.meta_item_list()) @@ -222,7 +398,7 @@ }; self.emit_repr_error( hint.span(), - item.span, + *span, &format!("attribute should be applied to {}", allowed_targets), &format!("not {} {}", article, allowed_targets), ) @@ -241,7 +417,7 @@ // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8) if (int_reprs > 1) || (is_simd && is_c) - || (int_reprs == 1 && is_c && is_c_like_enum(item)) { + || (int_reprs == 1 && is_c && item.map_or(false, |item| is_c_like_enum(item))) { let hint_spans: Vec<_> = hint_spans.collect(); span_warn!(self.tcx.sess, hint_spans, E0566, "conflicting representation hints"); @@ -262,10 +438,10 @@ fn check_stmt_attributes(&self, stmt: &hir::Stmt) { // When checking statements ignore expressions, they will be checked later - if let hir::StmtKind::Local(ref l) = stmt.node { + if let hir::StmtKind::Local(ref l) = stmt.kind { for attr in l.attrs.iter() { if attr.check_name(sym::inline) { - self.check_inline(attr, &stmt.span, Target::Statement); + self.check_inline(DUMMY_HIR_ID, attr, &stmt.span, Target::Statement); } if attr.check_name(sym::repr) { self.emit_repr_error( @@ -280,13 +456,13 @@ } fn check_expr_attributes(&self, expr: &hir::Expr) { - let target = match expr.node { + let target = match expr.kind { hir::ExprKind::Closure(..) => Target::Closure, _ => Target::Expression, }; for attr in expr.attrs.iter() { if attr.check_name(sym::inline) { - self.check_inline(attr, &expr.span, target); + self.check_inline(DUMMY_HIR_ID, attr, &expr.span, target); } if attr.check_name(sym::repr) { self.emit_repr_error( @@ -299,8 +475,8 @@ } } - fn check_used(&self, item: &hir::Item, target: Target) { - for attr in &item.attrs { + fn check_used(&self, attrs: &HirVec<Attribute>, target: Target) { + for attr in attrs { if attr.check_name(sym::used) && target != Target::Static { self.tcx.sess .span_err(attr.span, "attribute must be applied to a `static` variable"); @@ -314,12 +490,29 @@ NestedVisitorMap::OnlyBodies(&self.tcx.hir()) } - fn visit_item(&mut self, item: &'tcx hir::Item) { + fn visit_item(&mut self, item: &'tcx Item) { let target = Target::from_item(item); - self.check_attributes(item, target); + self.check_attributes(item.hir_id, &item.attrs, &item.span, target, Some(item)); intravisit::walk_item(self, item) } + fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem) { + let target = Target::from_trait_item(trait_item); + self.check_attributes(trait_item.hir_id, &trait_item.attrs, &trait_item.span, target, None); + intravisit::walk_trait_item(self, trait_item) + } + + fn visit_foreign_item(&mut self, f_item: &'tcx hir::ForeignItem) { + let target = Target::from_foreign_item(f_item); + self.check_attributes(f_item.hir_id, &f_item.attrs, &f_item.span, target, None); + intravisit::walk_foreign_item(self, f_item) + } + + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { + let target = Target::from_impl_item(self.tcx, impl_item); + self.check_attributes(impl_item.hir_id, &impl_item.attrs, &impl_item.span, target, None); + intravisit::walk_impl_item(self, impl_item) + } fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) { self.check_stmt_attributes(stmt); @@ -332,12 +525,12 @@ } } -fn is_c_like_enum(item: &hir::Item) -> bool { - if let hir::ItemKind::Enum(ref def, _) = item.node { +fn is_c_like_enum(item: &Item) -> bool { + if let ItemKind::Enum(ref def, _) = item.kind { for variant in &def.variants { match variant.data { hir::VariantData::Unit(..) => { /* continue */ } - _ => { return false; } + _ => return false, } } true
diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs index f7d31ca..d4d7af9 100644 --- a/src/librustc/hir/def.rs +++ b/src/librustc/hir/def.rs
@@ -6,8 +6,8 @@ use crate::util::nodemap::DefIdMap; use syntax::ast; -use syntax::ext::base::MacroKind; use syntax::ast::NodeId; +use syntax_pos::hygiene::MacroKind; use syntax_pos::Span; use rustc_macros::HashStable;
diff --git a/src/librustc/hir/def_id.rs b/src/librustc/hir/def_id.rs index d0bdc14..13200b3 100644 --- a/src/librustc/hir/def_id.rs +++ b/src/librustc/hir/def_id.rs
@@ -1,9 +1,9 @@ use crate::ty::{self, TyCtxt}; -use rustc_data_structures::indexed_vec::Idx; +use rustc_index::vec::Idx; use std::fmt; use std::u32; -newtype_index! { +rustc_index::newtype_index! { pub struct CrateId { ENCODABLE = custom } @@ -87,7 +87,7 @@ impl rustc_serialize::UseSpecializedEncodable for CrateNum {} impl rustc_serialize::UseSpecializedDecodable for CrateNum {} -newtype_index! { +rustc_index::newtype_index! { /// A DefIndex is an index into the hir-map for a crate, identifying a /// particular definition. It should really be considered an interned /// shorthand for a particular DefPath.
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 1f125de..920635d 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs
@@ -203,7 +203,7 @@ /// Invoked to visit the body of a function, method or closure. Like /// visit_nested_item, does nothing by default unless you override - /// `nested_visit_map` to return other htan `None`, in which case it will walk + /// `nested_visit_map` to return other than `None`, in which case it will walk /// the body. fn visit_nested_body(&mut self, id: BodyId) { let opt_body = self.nested_visit_map().intra().map(|map| map.body(id)); @@ -465,7 +465,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) { visitor.visit_vis(&item.vis); visitor.visit_ident(item.ident); - match item.node { + match item.kind { ItemKind::ExternCrate(orig_name) => { visitor.visit_id(item.hir_id); if let Some(orig_name) = orig_name { @@ -594,7 +594,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) { visitor.visit_id(typ.hir_id); - match typ.node { + match typ.kind { TyKind::Slice(ref ty) => { visitor.visit_ty(ty) } @@ -633,9 +633,6 @@ TyKind::Typeof(ref expression) => { visitor.visit_anon_const(expression) } - TyKind::CVarArgs(ref lt) => { - visitor.visit_lifetime(lt) - } TyKind::Infer | TyKind::Err => {} } } @@ -696,7 +693,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) { visitor.visit_id(pattern.hir_id); - match pattern.node { + match pattern.kind { PatKind::TupleStruct(ref qpath, ref children, _) => { visitor.visit_qpath(qpath, pattern.hir_id, pattern.span); walk_list!(visitor, visit_pat, children); @@ -743,7 +740,7 @@ visitor.visit_vis(&foreign_item.vis); visitor.visit_ident(foreign_item.ident); - match foreign_item.node { + match foreign_item.kind { ForeignItemKind::Fn(ref function_declaration, ref param_names, ref generics) => { visitor.visit_generics(generics); visitor.visit_fn_decl(function_declaration); @@ -856,7 +853,7 @@ visitor.visit_ident(trait_item.ident); walk_list!(visitor, visit_attribute, &trait_item.attrs); visitor.visit_generics(&trait_item.generics); - match trait_item.node { + match trait_item.kind { TraitItemKind::Const(ref ty, default) => { visitor.visit_id(trait_item.hir_id); visitor.visit_ty(ty); @@ -905,7 +902,7 @@ ref defaultness, ref attrs, ref generics, - ref node, + ref kind, span: _, } = *impl_item; @@ -914,7 +911,7 @@ visitor.visit_defaultness(defaultness); walk_list!(visitor, visit_attribute, attrs); visitor.visit_generics(generics); - match *node { + match *kind { ImplItemKind::Const(ref ty, body) => { visitor.visit_id(impl_item.hir_id); visitor.visit_ty(ty); @@ -974,7 +971,7 @@ pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt) { visitor.visit_id(statement.hir_id); - match statement.node { + match statement.kind { StmtKind::Local(ref local) => visitor.visit_local(local), StmtKind::Item(item) => visitor.visit_nested_item(item), StmtKind::Expr(ref expression) | @@ -992,7 +989,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_id(expression.hir_id); walk_list!(visitor, visit_attribute, expression.attrs.iter()); - match expression.node { + match expression.kind { ExprKind::Box(ref subexpression) => { visitor.visit_expr(subexpression) } @@ -1103,7 +1100,7 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm) { visitor.visit_id(arm.hir_id); - walk_list!(visitor, visit_pat, &arm.pats); + visitor.visit_pat(&arm.pat); if let Some(ref g) = arm.guard { match g { Guard::If(ref e) => visitor.visit_expr(e),
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 2ad5424..e665e05 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs
@@ -43,6 +43,7 @@ use crate::hir::def::{Namespace, Res, DefKind, PartialRes, PerNS}; use crate::hir::{GenericArg, ConstArg}; use crate::hir::ptr::P; +use crate::lint; use crate::lint::builtin::{self, PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES, ELIDED_LIFETIMES_IN_PATHS}; use crate::middle::cstore::CrateStore; @@ -52,7 +53,7 @@ use crate::util::nodemap::{DefIdMap, NodeMap}; use errors::Applicability; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc_index::vec::IndexVec; use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::sync::Lrc; @@ -64,14 +65,15 @@ use syntax::ptr::P as AstP; use syntax::ast::*; use syntax::errors; -use syntax::ext::base::SpecialDerives; -use syntax::ext::hygiene::ExpnId; +use syntax::expand::SpecialDerives; use syntax::print::pprust; +use syntax::parse::token::{self, Nonterminal, Token}; +use syntax::tokenstream::{TokenStream, TokenTree}; +use syntax::sess::ParseSess; use syntax::source_map::{respan, ExpnData, ExpnKind, DesugaringKind, Spanned}; use syntax::symbol::{kw, sym, Symbol}; -use syntax::tokenstream::{TokenStream, TokenTree}; -use syntax::parse::token::{self, Token}; use syntax::visit::{self, Visitor}; +use syntax_pos::hygiene::ExpnId; use syntax_pos::Span; const HIR_ID_COUNTER_LOCKED: u32 = 0xFFFFFFFF; @@ -82,10 +84,13 @@ /// Used to assign IDs to HIR nodes that do not directly correspond to AST nodes. sess: &'a Session, - cstore: &'a dyn CrateStore, - resolver: &'a mut dyn Resolver, + /// HACK(Centril): there is a cyclic dependency between the parser and lowering + /// if we don't have this function pointer. To avoid that dependency so that + /// librustc is independent of the parser, we use dynamic dispatch here. + nt_to_tokenstream: NtToTokenstream, + /// The items being lowered are collected here. items: BTreeMap<hir::HirId, hir::Item>, @@ -97,7 +102,7 @@ trait_impls: BTreeMap<DefId, Vec<hir::HirId>>, - modules: BTreeMap<NodeId, hir::ModuleItems>, + modules: BTreeMap<hir::HirId, hir::ModuleItems>, generator_kind: Option<hir::GeneratorKind>, @@ -141,7 +146,7 @@ /// vector. in_scope_lifetimes: Vec<ParamName>, - current_module: NodeId, + current_module: hir::HirId, type_def_lifetime_params: DefIdMap<usize>, @@ -154,6 +159,8 @@ } pub trait Resolver { + fn cstore(&self) -> &dyn CrateStore; + /// Obtains resolution for a `NodeId` with a single resolution. fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes>; @@ -178,8 +185,12 @@ ) -> (ast::Path, Res<NodeId>); fn has_derives(&self, node_id: NodeId, derives: SpecialDerives) -> bool; + + fn lint_buffer(&mut self) -> &mut lint::LintBuffer; } +type NtToTokenstream = fn(&Nonterminal, &ParseSess, Span) -> TokenStream; + /// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree, /// and if so, what meaning it has. #[derive(Debug)] @@ -232,21 +243,23 @@ pub fn lower_crate( sess: &Session, - cstore: &dyn CrateStore, dep_graph: &DepGraph, krate: &Crate, resolver: &mut dyn Resolver, + nt_to_tokenstream: NtToTokenstream, ) -> hir::Crate { // We're constructing the HIR here; we don't care what we will // read, since we haven't even constructed the *input* to // incr. comp. yet. dep_graph.assert_ignored(); + let _prof_timer = sess.prof.generic_activity("hir_lowering"); + LoweringContext { crate_root: sess.parse_sess.injected_crate_name.try_get().copied(), sess, - cstore, resolver, + nt_to_tokenstream, items: BTreeMap::new(), trait_items: BTreeMap::new(), impl_items: BTreeMap::new(), @@ -262,7 +275,7 @@ is_in_dyn_type: false, anonymous_lifetime_mode: AnonymousLifetimeMode::PassThrough, type_def_lifetime_params: Default::default(), - current_module: CRATE_NODE_ID, + current_module: hir::CRATE_HIR_ID, current_hir_id_owner: vec![(CRATE_DEF_INDEX, 0)], item_local_id_counters: Default::default(), node_id_to_hir_id: IndexVec::new(), @@ -346,7 +359,7 @@ impl<'a, 'b> Visitor<'a> for ImplTraitTypeIdVisitor<'b> { fn visit_ty(&mut self, ty: &'a Ty) { - match ty.node { + match ty.kind { | TyKind::Typeof(_) | TyKind::BareFn(_) => return, @@ -425,7 +438,7 @@ impl<'tcx, 'interner> Visitor<'tcx> for MiscCollector<'tcx, 'interner> { fn visit_pat(&mut self, p: &'tcx Pat) { - if let PatKind::Paren(..) | PatKind::Rest = p.node { + if let PatKind::Paren(..) | PatKind::Rest = p.kind { // Doesn't generate a HIR node } else if let Some(owner) = self.hir_id_owner { self.lctx.lower_node_id_with_owner(p.id, owner); @@ -434,39 +447,10 @@ visit::walk_pat(self, p) } - // HACK(or_patterns; Centril | dlrobertson): Avoid creating - // HIR nodes for `PatKind::Or` for the top level of a `ast::Arm`. - // This is a temporary hack that should go away once we push down - // `arm.pats: HirVec<P<Pat>>` -> `arm.pat: P<Pat>` to HIR. // Centril - fn visit_arm(&mut self, arm: &'tcx Arm) { - match &arm.pat.node { - PatKind::Or(pats) => pats.iter().for_each(|p| self.visit_pat(p)), - _ => self.visit_pat(&arm.pat), - } - walk_list!(self, visit_expr, &arm.guard); - self.visit_expr(&arm.body); - walk_list!(self, visit_attribute, &arm.attrs); - } - - // HACK(or_patterns; Centril | dlrobertson): Same as above. // Centril - fn visit_expr(&mut self, e: &'tcx Expr) { - if let ExprKind::Let(pat, scrutinee) = &e.node { - walk_list!(self, visit_attribute, e.attrs.iter()); - match &pat.node { - PatKind::Or(pats) => pats.iter().for_each(|p| self.visit_pat(p)), - _ => self.visit_pat(&pat), - } - self.visit_expr(scrutinee); - self.visit_expr_post(e); - return; - } - visit::walk_expr(self, e) - } - fn visit_item(&mut self, item: &'tcx Item) { let hir_id = self.lctx.allocate_hir_id_counter(item.id); - match item.node { + match item.kind { ItemKind::Struct(_, ref generics) | ItemKind::Union(_, ref generics) | ItemKind::Enum(_, ref generics) @@ -498,7 +482,7 @@ fn visit_trait_item(&mut self, item: &'tcx TraitItem) { self.lctx.allocate_hir_id_counter(item.id); - match item.node { + match item.kind { TraitItemKind::Method(_, None) => { // Ignore patterns in trait methods without bodies self.with_hir_id_owner(None, |this| { @@ -526,7 +510,7 @@ } fn visit_ty(&mut self, t: &'tcx Ty) { - match t.node { + match t.kind { // Mirrors the case in visit::walk_ty TyKind::BareFn(ref f) => { walk_list!( @@ -809,15 +793,15 @@ // really show up for end-user. let (str_name, kind) = match hir_name { ParamName::Plain(ident) => ( - ident.as_interned_str(), + ident.name, hir::LifetimeParamKind::InBand, ), ParamName::Fresh(_) => ( - kw::UnderscoreLifetime.as_interned_str(), + kw::UnderscoreLifetime, hir::LifetimeParamKind::Elided, ), ParamName::Error => ( - kw::UnderscoreLifetime.as_interned_str(), + kw::UnderscoreLifetime, hir::LifetimeParamKind::Error, ), }; @@ -997,7 +981,7 @@ if id.is_local() { self.resolver.definitions().def_key(id.index) } else { - self.cstore.def_key(id) + self.resolver.cstore().def_key(id) } } @@ -1017,10 +1001,12 @@ // lower attributes (we use the AST version) there is nowhere to keep // the `HirId`s. We don't actually need HIR version of attributes anyway. Attribute { + item: AttrItem { + path: attr.path.clone(), + tokens: self.lower_token_stream(attr.tokens.clone()), + }, id: attr.id, style: attr.style, - path: attr.path.clone(), - tokens: self.lower_token_stream(attr.tokens.clone()), is_sugared_doc: attr.is_sugared_doc, span: attr.span, } @@ -1047,7 +1033,7 @@ fn lower_token(&mut self, token: Token) -> TokenStream { match token.kind { token::Interpolated(nt) => { - let tts = nt.to_tokenstream(&self.sess.parse_sess, token.span); + let tts = (self.nt_to_tokenstream)(&nt, &self.sess.parse_sess, token.span); self.lower_token_stream(tts) } _ => TokenTree::Token(token).into(), @@ -1133,7 +1119,7 @@ let ty = this.lower_ty( &Ty { id: this.sess.next_node_id(), - node: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()), + kind: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()), span: constraint.span, }, itctx, @@ -1194,14 +1180,14 @@ let id = self.lower_node_id(t.id); let qpath = self.lower_qpath(t.id, qself, path, param_mode, itctx); let ty = self.ty_path(id, t.span, qpath); - if let hir::TyKind::TraitObject(..) = ty.node { + if let hir::TyKind::TraitObject(..) = ty.kind { self.maybe_lint_bare_trait(t.span, t.id, qself.is_none() && path.is_global()); } ty } fn lower_ty_direct(&mut self, t: &Ty, mut itctx: ImplTraitContext<'_>) -> hir::Ty { - let kind = match t.node { + let kind = match t.kind { TyKind::Infer => hir::TyKind::Infer, TyKind::Err => hir::TyKind::Err, TyKind::Slice(ref ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), @@ -1364,17 +1350,12 @@ } } } - TyKind::Mac(_) => bug!("`TyMac` should have been expanded by now"), - TyKind::CVarArgs => { - // Create the implicit lifetime of the "spoofed" `VaListImpl`. - let span = self.sess.source_map().next_point(t.span.shrink_to_lo()); - let lt = self.new_implicit_lifetime(span); - hir::TyKind::CVarArgs(lt) - }, + TyKind::Mac(_) => bug!("`TyKind::Mac` should have been expanded by now"), + TyKind::CVarArgs => bug!("`TyKind::CVarArgs` should have been handled elsewhere"), }; hir::Ty { - node: kind, + kind, span: t.span, hir_id: self.lower_node_id(t.id), } @@ -1474,7 +1455,7 @@ hir_id: opaque_ty_id, ident: Ident::invalid(), attrs: Default::default(), - node: opaque_ty_item_kind, + kind: opaque_ty_item_kind, vis: respan(span.shrink_to_lo(), hir::VisibilityKind::Inherited), span: opaque_ty_span, }; @@ -1534,7 +1515,7 @@ fn visit_ty(&mut self, t: &'v hir::Ty) { // Don't collect elided lifetimes used inside of `fn()` syntax. - if let hir::TyKind::BareFn(_) = t.node { + if let hir::TyKind::BareFn(_) = t.kind { let old_collect_elided_lifetimes = self.collect_elided_lifetimes; self.collect_elided_lifetimes = false; @@ -1610,7 +1591,7 @@ self.context.resolver.definitions().create_def_with_parent( self.parent, def_node_id, - DefPathData::LifetimeNs(name.ident().as_interned_str()), + DefPathData::LifetimeNs(name.ident().name), ExpnId::root(), lifetime.span); @@ -1747,8 +1728,8 @@ return n; } assert!(!def_id.is_local()); - let item_generics = - self.cstore.item_generics_cloned_untracked(def_id, self.sess); + let item_generics = self.resolver.cstore() + .item_generics_cloned_untracked(def_id, self.sess); let n = item_generics.own_counts().lifetimes; self.type_def_lifetime_params.insert(def_id, n); n @@ -1879,7 +1860,7 @@ GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args { ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data), ParenthesizedGenericArgs::Warn => { - self.sess.buffer_lint( + self.resolver.lint_buffer().buffer_lint( PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES, CRATE_NODE_ID, data.span, @@ -1893,15 +1874,16 @@ if let Ok(snippet) = self.sess.source_map().span_to_snippet(data.span) { // Do not suggest going from `Trait()` to `Trait<>` if data.inputs.len() > 0 { - let split = snippet.find('(').unwrap(); - let trait_name = &snippet[0..split]; - let args = &snippet[split + 1 .. snippet.len() - 1]; - err.span_suggestion( - data.span, - "use angle brackets instead", - format!("{}<{}>", trait_name, args), - Applicability::MaybeIncorrect, - ); + if let Some(split) = snippet.find('(') { + let trait_name = &snippet[0..split]; + let args = &snippet[split + 1 .. snippet.len() - 1]; + err.span_suggestion( + data.span, + "use angle brackets instead", + format!("{}<{}>", trait_name, args), + Applicability::MaybeIncorrect, + ); + } } }; err.emit(); @@ -1975,7 +1957,7 @@ } AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => { - self.sess.buffer_lint_with_diagnostic( + self.resolver.lint_buffer().buffer_lint_with_diagnostic( ELIDED_LIFETIMES_IN_PATHS, CRATE_NODE_ID, path_span, @@ -2055,7 +2037,7 @@ .map(|ty| this.lower_ty_direct(ty, ImplTraitContext::disallowed())) .collect(); let mk_tup = |this: &mut Self, tys, span| { - hir::Ty { node: hir::TyKind::Tup(tys), hir_id: this.next_id(), span } + hir::Ty { kind: hir::TyKind::Tup(tys), hir_id: this.next_id(), span } }; ( hir::GenericArgs { @@ -2122,9 +2104,16 @@ } fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> hir::HirVec<Ident> { - decl.inputs + // Skip the `...` (`CVarArgs`) trailing arguments from the AST, + // as they are not explicit in HIR/Ty function signatures. + // (instead, the `c_variadic` flag is set to `true`) + let mut inputs = &decl.inputs[..]; + if decl.c_variadic() { + inputs = &inputs[..inputs.len() - 1]; + } + inputs .iter() - .map(|param| match param.pat.node { + .map(|param| match param.pat.kind { PatKind::Ident(_, ident, _) => ident, _ => Ident::new(kw::Invalid, param.pat.span), }) @@ -2159,10 +2148,19 @@ self.anonymous_lifetime_mode }; + let c_variadic = decl.c_variadic(); + // Remember how many lifetimes were already around so that we can // only look at the lifetime parameters introduced by the arguments. let inputs = self.with_anonymous_lifetime_mode(lt_mode, |this| { - decl.inputs + // Skip the `...` (`CVarArgs`) trailing arguments from the AST, + // as they are not explicit in HIR/Ty function signatures. + // (instead, the `c_variadic` flag is set to `true`) + let mut inputs = &decl.inputs[..]; + if c_variadic { + inputs = &inputs[..inputs.len() - 1]; + } + inputs .iter() .map(|param| { if let Some((_, ibty)) = &mut in_band_ty_params { @@ -2184,9 +2182,7 @@ match decl.output { FunctionRetTy::Ty(ref ty) => match in_band_ty_params { Some((def_id, _)) if impl_trait_return_allow => { - hir::Return(self.lower_ty(ty, - ImplTraitContext::OpaqueTy(Some(def_id)) - )) + hir::Return(self.lower_ty(ty, ImplTraitContext::OpaqueTy(Some(def_id)))) } _ => { hir::Return(self.lower_ty(ty, ImplTraitContext::disallowed())) @@ -2199,27 +2195,27 @@ P(hir::FnDecl { inputs, output, - c_variadic: decl.c_variadic, + c_variadic, implicit_self: decl.inputs.get(0).map_or( hir::ImplicitSelfKind::None, |arg| { - let is_mutable_pat = match arg.pat.node { + let is_mutable_pat = match arg.pat.kind { PatKind::Ident(BindingMode::ByValue(mt), _, _) | PatKind::Ident(BindingMode::ByRef(mt), _, _) => mt == Mutability::Mutable, _ => false, }; - match arg.ty.node { + match arg.ty.kind { TyKind::ImplicitSelf if is_mutable_pat => hir::ImplicitSelfKind::Mut, TyKind::ImplicitSelf => hir::ImplicitSelfKind::Imm, // Given we are only considering `ImplicitSelf` types, we needn't consider // the case where we have a mutable pattern to a reference as that would // no longer be an `ImplicitSelf`. - TyKind::Rptr(_, ref mt) if mt.ty.node.is_implicit_self() && + TyKind::Rptr(_, ref mt) if mt.ty.kind.is_implicit_self() && mt.mutbl == ast::Mutability::Mutable => hir::ImplicitSelfKind::MutRef, - TyKind::Rptr(_, ref mt) if mt.ty.node.is_implicit_self() => + TyKind::Rptr(_, ref mt) if mt.ty.kind.is_implicit_self() => hir::ImplicitSelfKind::ImmRef, _ => hir::ImplicitSelfKind::None, } @@ -2434,7 +2430,7 @@ let opaque_ty_ref = hir::TyKind::Def(hir::ItemId { id: opaque_ty_id }, generic_args.into()); hir::FunctionRetTy::Return(P(hir::Ty { - node: opaque_ty_ref, + kind: opaque_ty_ref, span, hir_id: self.next_id(), })) @@ -2455,7 +2451,7 @@ FunctionRetTy::Default(ret_ty_span) => { P(hir::Ty { hir_id: self.next_id(), - node: hir::TyKind::Tup(hir_vec![]), + kind: hir::TyKind::Tup(hir_vec![]), span: *ret_ty_span, }) } @@ -2691,7 +2687,7 @@ for (index, stmt) in b.stmts.iter().enumerate() { if index == b.stmts.len() - 1 { - if let StmtKind::Expr(ref e) = stmt.node { + if let StmtKind::Expr(ref e) = stmt.kind { expr = Some(P(self.lower_expr(e))); } else { stmts.extend(self.lower_stmt(stmt)); @@ -2719,7 +2715,7 @@ } fn lower_pat(&mut self, p: &Pat) -> P<hir::Pat> { - let node = match p.node { + let node = match p.kind { PatKind::Wild => hir::PatKind::Wild, PatKind::Ident(ref binding_mode, ident, ref sub) => { let lower_sub = |this: &mut Self| sub.as_ref().map(|x| this.lower_pat(x)); @@ -2836,7 +2832,7 @@ let mut iter = pats.iter(); while let Some(pat) = iter.next() { // Interpret the first `((ref mut?)? x @)? ..` pattern as a subslice pattern. - match pat.node { + match pat.kind { PatKind::Rest => { prev_rest_span = Some(pat.span); slice = Some(self.pat_wild_with_node_id_of(pat)); @@ -2858,7 +2854,7 @@ while let Some(pat) = iter.next() { // There was a previous subslice pattern; make sure we don't allow more. - let rest_span = match pat.node { + let rest_span = match pat.kind { PatKind::Rest => Some(pat.span), PatKind::Ident(.., Some(ref sub)) if sub.is_rest() => { // The `HirValidator` is merciless; add a `_` pattern to avoid ICEs. @@ -2915,10 +2911,10 @@ } /// Construct a `Pat` with the `HirId` of `p.id` lowered. - fn pat_with_node_id_of(&mut self, p: &Pat, node: hir::PatKind) -> P<hir::Pat> { + fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind) -> P<hir::Pat> { P(hir::Pat { hir_id: self.lower_node_id(p.id), - node, + kind, span: p.span, }) } @@ -2962,7 +2958,7 @@ } fn lower_stmt(&mut self, s: &Stmt) -> SmallVec<[hir::Stmt; 1]> { - let node = match s.node { + let kind = match s.kind { StmtKind::Local(ref l) => { let (l, item_ids) = self.lower_local(l); let mut ids: SmallVec<[hir::Stmt; 1]> = item_ids @@ -2975,7 +2971,7 @@ ids.push({ hir::Stmt { hir_id: self.lower_node_id(s.id), - node: hir::StmtKind::Local(P(l)), + kind: hir::StmtKind::Local(P(l)), span: s.span, } }); @@ -2993,7 +2989,7 @@ hir::Stmt { hir_id, - node: hir::StmtKind::Item(item_id), + kind: hir::StmtKind::Item(item_id), span: s.span, } }) @@ -3005,7 +3001,7 @@ }; smallvec![hir::Stmt { hir_id: self.lower_node_id(s.id), - node, + kind, span: s.span, }] } @@ -3042,8 +3038,8 @@ // Helper methods for building HIR. - fn stmt(&mut self, span: Span, node: hir::StmtKind) -> hir::Stmt { - hir::Stmt { span, node, hir_id: self.next_id() } + fn stmt(&mut self, span: Span, kind: hir::StmtKind) -> hir::Stmt { + hir::Stmt { span, kind, hir_id: self.next_id() } } fn stmt_expr(&mut self, span: Span, expr: hir::Expr) -> hir::Stmt { @@ -3143,7 +3139,7 @@ ( P(hir::Pat { hir_id, - node: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None), + kind: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None), span, }), hir_id @@ -3154,10 +3150,10 @@ self.pat(span, hir::PatKind::Wild) } - fn pat(&mut self, span: Span, pat: hir::PatKind) -> P<hir::Pat> { + fn pat(&mut self, span: Span, kind: hir::PatKind) -> P<hir::Pat> { P(hir::Pat { hir_id: self.next_id(), - node: pat, + kind, span, }) } @@ -3195,7 +3191,7 @@ } fn ty_path(&mut self, mut hir_id: hir::HirId, span: Span, qpath: hir::QPath) -> hir::Ty { - let node = match qpath { + let kind = match qpath { hir::QPath::Resolved(None, path) => { // Turn trait object paths into `TyKind::TraitObject` instead. match path.res { @@ -3219,9 +3215,10 @@ } _ => hir::TyKind::Path(qpath), }; + hir::Ty { hir_id, - node, + kind, span, } } @@ -3296,10 +3293,14 @@ let id = self.sess.next_node_id(); self.new_named_lifetime(id, span, hir::LifetimeName::Error) } - // This is the normal case. - AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span), - - AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span), + // `PassThrough` is the normal case. + // `new_error_lifetime`, which would usually be used in the case of `ReportError`, + // is unsuitable here, as these can occur from missing lifetime parameters in a + // `PathSegment`, for which there is no associated `'_` or `&T` with no explicit + // lifetime. Instead, we simply create an implicit lifetime, which will be checked + // later, at which point a suitable error will be emitted. + | AnonymousLifetimeMode::PassThrough + | AnonymousLifetimeMode::ReportError => self.new_implicit_lifetime(span), } } @@ -3349,7 +3350,7 @@ } } - fn maybe_lint_bare_trait(&self, span: Span, id: NodeId, is_global: bool) { + fn maybe_lint_bare_trait(&mut self, span: Span, id: NodeId, is_global: bool) { // FIXME(davidtwco): This is a hack to detect macros which produce spans of the // call site which do not have a macro backtrace. See #61963. let is_macro_callsite = self.sess.source_map() @@ -3357,7 +3358,7 @@ .map(|snippet| snippet.starts_with("#[")) .unwrap_or(true); if !is_macro_callsite { - self.sess.buffer_lint_with_diagnostic( + self.resolver.lint_buffer().buffer_lint_with_diagnostic( builtin::BARE_TRAIT_OBJECTS, id, span, @@ -3409,7 +3410,7 @@ } }; - match expr.node { + match expr.kind { // All built-in range literals but `..=` and `..` desugar to `Struct`s. ExprKind::Struct(ref qpath, _, _) => { if let QPath::Resolved(None, ref path) = **qpath { @@ -3424,8 +3425,8 @@ // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. ExprKind::Call(ref func, _) => { - if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.node { - if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.node { + if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.kind { + if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.kind { let new_call = segment.ident.as_str() == "new"; return is_range_path(&path) && is_lit(sess, &expr.span) && new_call; }
diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs index 990728f..73db762 100644 --- a/src/librustc/hir/lowering/expr.rs +++ b/src/librustc/hir/lowering/expr.rs
@@ -17,7 +17,7 @@ } pub(super) fn lower_expr(&mut self, e: &Expr) -> hir::Expr { - let kind = match e.node { + let kind = match e.kind { ExprKind::Box(ref inner) => hir::ExprKind::Box(P(self.lower_expr(inner))), ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), ExprKind::Repeat(ref expr, ref count) => { @@ -54,7 +54,7 @@ let ohs = P(self.lower_expr(ohs)); hir::ExprKind::Unary(op, ohs) } - ExprKind::Lit(ref l) => hir::ExprKind::Lit(respan(l.span, l.node.clone())), + ExprKind::Lit(ref l) => hir::ExprKind::Lit(respan(l.span, l.kind.clone())), ExprKind::Cast(ref expr, ref ty) => { let expr = P(self.lower_expr(expr)); hir::ExprKind::Cast(expr, self.lower_ty(ty, ImplTraitContext::disallowed())) @@ -89,9 +89,14 @@ hir::MatchSource::Normal, ), ExprKind::Async(capture_clause, closure_node_id, ref block) => { - self.make_async_expr(capture_clause, closure_node_id, None, block.span, |this| { - this.with_new_scopes(|this| this.lower_block_expr(block)) - }) + self.make_async_expr( + capture_clause, + closure_node_id, + None, + block.span, + hir::AsyncGeneratorKind::Block, + |this| this.with_new_scopes(|this| this.lower_block_expr(block)), + ) } ExprKind::Await(ref expr) => self.lower_expr_await(e.span, expr), ExprKind::Closure( @@ -184,7 +189,7 @@ hir::Expr { hir_id: self.lower_node_id(e.id), - node: kind, + kind, span: e.span, attrs: e.attrs.clone(), } @@ -230,11 +235,20 @@ /// ``` fn lower_expr_let(&mut self, span: Span, pat: &Pat, scrutinee: &Expr) -> hir::ExprKind { // If we got here, the `let` expression is not allowed. - self.sess - .struct_span_err(span, "`let` expressions are not supported here") - .note("only supported directly in conditions of `if`- and `while`-expressions") - .note("as well as when nested within `&&` and parenthesis in those conditions") - .emit(); + + if self.sess.opts.unstable_features.is_nightly_build() { + self.sess + .struct_span_err(span, "`let` expressions are not supported here") + .note("only supported directly in conditions of `if`- and `while`-expressions") + .note("as well as when nested within `&&` and parenthesis in those conditions") + .emit(); + } + else { + self.sess + .struct_span_err(span, "expected expression, found statement (`let`)") + .note("variable declaration using `let` is a statement") + .emit(); + } // For better recovery, we emit: // ``` @@ -247,14 +261,14 @@ // 4. The return type of the block is `bool` which seems like what the user wanted. let scrutinee = self.lower_expr(scrutinee); let then_arm = { - let pat = self.lower_pat_top_hack(pat); + let pat = self.lower_pat(pat); let expr = self.expr_bool(span, true); self.arm(pat, P(expr)) }; let else_arm = { let pat = self.pat_wild(span); let expr = self.expr_bool(span, false); - self.arm(hir_vec![pat], P(expr)) + self.arm(pat, P(expr)) }; hir::ExprKind::Match( P(scrutinee), @@ -278,15 +292,15 @@ None => (self.expr_block_empty(span), false), Some(els) => (self.lower_expr(els), true), }; - let else_arm = self.arm(hir_vec![else_pat], P(else_expr)); + let else_arm = self.arm(else_pat, P(else_expr)); // Handle then + scrutinee: let then_expr = self.lower_block_expr(then); - let (then_pat, scrutinee, desugar) = match cond.node { + let (then_pat, scrutinee, desugar) = match cond.kind { // `<pat> => <then>`: ExprKind::Let(ref pat, ref scrutinee) => { let scrutinee = self.lower_expr(scrutinee); - let pat = self.lower_pat_top_hack(pat); + let pat = self.lower_pat(pat); (pat, scrutinee, hir::MatchSource::IfLetDesugar { contains_else_clause }) } // `true => <then>`: @@ -303,7 +317,7 @@ // let temporaries live outside of `cond`. let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new()); let pat = self.pat_bool(span, true); - (hir_vec![pat], cond, hir::MatchSource::IfDesugar { contains_else_clause }) + (pat, cond, hir::MatchSource::IfDesugar { contains_else_clause }) } }; let then_arm = self.arm(then_pat, P(then_expr)); @@ -327,12 +341,12 @@ let else_arm = { let else_pat = self.pat_wild(span); let else_expr = self.expr_break(span, ThinVec::new()); - self.arm(hir_vec![else_pat], else_expr) + self.arm(else_pat, else_expr) }; // Handle then + scrutinee: let then_expr = self.lower_block_expr(body); - let (then_pat, scrutinee, desugar, source) = match cond.node { + let (then_pat, scrutinee, desugar, source) = match cond.kind { ExprKind::Let(ref pat, ref scrutinee) => { // to: // @@ -343,7 +357,7 @@ // } // } let scrutinee = self.with_loop_condition_scope(|t| t.lower_expr(scrutinee)); - let pat = self.lower_pat_top_hack(pat); + let pat = self.lower_pat(pat); (pat, scrutinee, hir::MatchSource::WhileLetDesugar, hir::LoopSource::WhileLet) } _ => { @@ -371,7 +385,7 @@ let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new()); // `true => <then>`: let pat = self.pat_bool(span, true); - (hir_vec![pat], cond, hir::MatchSource::WhileDesugar, hir::LoopSource::While) + (pat, cond, hir::MatchSource::WhileDesugar, hir::LoopSource::While) } }; let then_arm = self.arm(then_pat, P(then_expr)); @@ -392,19 +406,35 @@ ) } + /// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_ok(<expr>) }`, + /// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_ok(()) }` + /// and save the block id to use it as a break target for desugaring of the `?` operator. fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind { self.with_catch_scope(body.id, |this| { - let unstable_span = this.mark_span_with_reason( + let mut block = this.lower_block(body, true).into_inner(); + + let try_span = this.mark_span_with_reason( DesugaringKind::TryBlock, body.span, this.allow_try_trait.clone(), ); - let mut block = this.lower_block(body, true).into_inner(); - let tail = block.expr.take().map_or_else( - || this.expr_unit(this.sess.source_map().end_point(unstable_span)), + + // Final expression of the block (if present) or `()` with span at the end of block + let tail_expr = block.expr.take().map_or_else( + || this.expr_unit(this.sess.source_map().end_point(try_span)), |x: P<hir::Expr>| x.into_inner(), ); - block.expr = Some(this.wrap_in_try_constructor(sym::from_ok, tail, unstable_span)); + + let ok_wrapped_span = this.mark_span_with_reason( + DesugaringKind::TryBlock, + tail_expr.span, + None + ); + + // `::std::ops::Try::from_ok($tail_expr)` + block.expr = Some(this.wrap_in_try_constructor( + sym::from_ok, try_span, tail_expr, ok_wrapped_span)); + hir::ExprKind::Block(P(block), None) }) } @@ -412,19 +442,20 @@ fn wrap_in_try_constructor( &mut self, method: Symbol, - e: hir::Expr, - unstable_span: Span, + method_span: Span, + expr: hir::Expr, + overall_span: Span, ) -> P<hir::Expr> { let path = &[sym::ops, sym::Try, method]; - let from_err = P(self.expr_std_path(unstable_span, path, None, ThinVec::new())); - P(self.expr_call(e.span, from_err, hir_vec![e])) + let constructor = P(self.expr_std_path(method_span, path, None, ThinVec::new())); + P(self.expr_call(overall_span, constructor, hir_vec![expr])) } fn lower_arm(&mut self, arm: &Arm) -> hir::Arm { hir::Arm { hir_id: self.next_id(), attrs: self.lower_attrs(&arm.attrs), - pats: self.lower_pat_top_hack(&arm.pat), + pat: self.lower_pat(&arm.pat), guard: match arm.guard { Some(ref x) => Some(hir::Guard::If(P(self.lower_expr(x)))), _ => None, @@ -434,22 +465,13 @@ } } - /// HACK(or_patterns; Centril | dlrobertson): For now we don't push down top level or-patterns - /// `p | q` into `hir::PatKind::Or(...)` as post-lowering bits of the compiler are not ready - /// to deal with it. This should by fixed by pushing it down to HIR and then HAIR. - fn lower_pat_top_hack(&mut self, pat: &Pat) -> HirVec<P<hir::Pat>> { - match pat.node { - PatKind::Or(ref ps) => ps.iter().map(|x| self.lower_pat(x)).collect(), - _ => hir_vec![self.lower_pat(pat)], - } - } - pub(super) fn make_async_expr( &mut self, capture_clause: CaptureBy, closure_node_id: NodeId, ret_ty: Option<AstP<Ty>>, span: Span, + async_gen_kind: hir::AsyncGeneratorKind, body: impl FnOnce(&mut LoweringContext<'_>) -> hir::Expr, ) -> hir::ExprKind { let capture_clause = self.lower_capture_clause(capture_clause); @@ -460,16 +482,15 @@ let ast_decl = FnDecl { inputs: vec![], output, - c_variadic: false }; let decl = self.lower_fn_decl(&ast_decl, None, /* impl trait allowed */ false, None); let body_id = self.lower_fn_body(&ast_decl, |this| { - this.generator_kind = Some(hir::GeneratorKind::Async); + this.generator_kind = Some(hir::GeneratorKind::Async(async_gen_kind)); body(this) }); // `static || -> <ret_ty> { body }`: - let generator_node = hir::ExprKind::Closure( + let generator_kind = hir::ExprKind::Closure( capture_clause, decl, body_id, @@ -478,7 +499,7 @@ ); let generator = hir::Expr { hir_id: self.lower_node_id(closure_node_id), - node: generator_node, + kind: generator_kind, span, attrs: ThinVec::new(), }; @@ -516,7 +537,7 @@ /// ``` fn lower_expr_await(&mut self, await_span: Span, expr: &Expr) -> hir::ExprKind { match self.generator_kind { - Some(hir::GeneratorKind::Async) => {}, + Some(hir::GeneratorKind::Async(_)) => {}, Some(hir::GeneratorKind::Gen) | None => { let mut err = struct_span_err!( @@ -592,7 +613,7 @@ ); P(this.expr(await_span, expr_break, ThinVec::new())) }); - self.arm(hir_vec![ready_pat], break_x) + self.arm(ready_pat, break_x) }; // `::std::task::Poll::Pending => {}` @@ -603,7 +624,7 @@ hir_vec![], ); let empty_block = P(self.expr_block_empty(span)); - self.arm(hir_vec![pending_pat], empty_block) + self.arm(pending_pat, empty_block) }; let inner_match_stmt = { @@ -635,7 +656,7 @@ // loop { .. } let loop_expr = P(hir::Expr { hir_id: loop_hir_id, - node: hir::ExprKind::Loop( + kind: hir::ExprKind::Loop( loop_block, None, hir::LoopSource::Loop, @@ -645,7 +666,7 @@ }); // mut pinned => loop { ... } - let pinned_arm = self.arm(hir_vec![pinned_pat], loop_expr); + let pinned_arm = self.arm(pinned_pat, loop_expr); // match <expr> { // mut pinned => loop { .. } @@ -715,14 +736,13 @@ E0628, "generators cannot have explicit parameters" ); - self.sess.abort_if_errors(); } Some(match movability { Movability::Movable => hir::GeneratorMovability::Movable, Movability::Static => hir::GeneratorMovability::Static, }) }, - Some(hir::GeneratorKind::Async) => { + Some(hir::GeneratorKind::Async(_)) => { bug!("non-`async` closure body turned `async` during lowering"); }, None => { @@ -750,7 +770,6 @@ let outer_decl = FnDecl { inputs: decl.inputs.clone(), output: FunctionRetTy::Default(fn_decl_span), - c_variadic: false, }; // We need to lower the declaration outside the new scope, because we // have to conserve the state of being inside a loop condition for the @@ -782,10 +801,12 @@ None }; let async_body = this.make_async_expr( - capture_clause, closure_id, async_ret_ty, body.span, - |this| { - this.with_new_scopes(|this| this.lower_expr(body)) - } + capture_clause, + closure_id, + async_ret_ty, + body.span, + hir::AsyncGeneratorKind::Closure, + |this| this.with_new_scopes(|this| this.lower_expr(body)), ); this.expr(fn_decl_span, async_body, ThinVec::new()) }); @@ -1001,14 +1022,14 @@ fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind { match self.generator_kind { Some(hir::GeneratorKind::Gen) => {}, - Some(hir::GeneratorKind::Async) => { + Some(hir::GeneratorKind::Async(_)) => { span_err!( self.sess, span, E0727, "`async` generators are not yet supported", ); - self.sess.abort_if_errors(); + return hir::ExprKind::Err; }, None => self.generator_kind = Some(hir::GeneratorKind::Gen), } @@ -1050,10 +1071,9 @@ ) -> hir::Expr { // expand <head> let mut head = self.lower_expr(head); - let head_sp = head.span; let desugared_span = self.mark_span_with_reason( DesugaringKind::ForLoop, - head_sp, + head.span, None, ); head.span = desugared_span; @@ -1079,7 +1099,7 @@ ThinVec::new(), )); let some_pat = self.pat_some(pat.span, val_pat); - self.arm(hir_vec![some_pat], assign) + self.arm(some_pat, assign) }; // `::std::option::Option::None => break` @@ -1087,7 +1107,7 @@ let break_expr = self.with_loop_scope(e.id, |this| this.expr_break(e.span, ThinVec::new())); let pat = self.pat_none(e.span); - self.arm(hir_vec![pat], break_expr) + self.arm(pat, break_expr) }; // `mut iter` @@ -1099,21 +1119,21 @@ // `match ::std::iter::Iterator::next(&mut iter) { ... }` let match_expr = { - let iter = P(self.expr_ident(head_sp, iter, iter_pat_nid)); - let ref_mut_iter = self.expr_mut_addr_of(head_sp, iter); + let iter = P(self.expr_ident(desugared_span, iter, iter_pat_nid)); + let ref_mut_iter = self.expr_mut_addr_of(desugared_span, iter); let next_path = &[sym::iter, sym::Iterator, sym::next]; let next_expr = P(self.expr_call_std_path( - head_sp, + desugared_span, next_path, hir_vec![ref_mut_iter], )); let arms = hir_vec![pat_arm, break_arm]; - self.expr_match(head_sp, next_expr, arms, hir::MatchSource::ForLoopDesugar) + self.expr_match(desugared_span, next_expr, arms, hir::MatchSource::ForLoopDesugar) }; - let match_stmt = self.stmt_expr(head_sp, match_expr); + let match_stmt = self.stmt_expr(desugared_span, match_expr); - let next_expr = P(self.expr_ident(head_sp, next_ident, next_pat_hid)); + let next_expr = P(self.expr_ident(desugared_span, next_ident, next_pat_hid)); // `let mut __next` let next_let = self.stmt_let_pat( @@ -1128,7 +1148,7 @@ let pat = self.lower_pat(pat); let pat_let = self.stmt_let_pat( ThinVec::new(), - head_sp, + desugared_span, Some(next_expr), pat, hir::LocalSource::ForLoopDesugar, @@ -1145,34 +1165,34 @@ )); // `[opt_ident]: loop { ... }` - let loop_expr = hir::ExprKind::Loop( + let kind = hir::ExprKind::Loop( loop_block, self.lower_label(opt_label), hir::LoopSource::ForLoop, ); let loop_expr = P(hir::Expr { hir_id: self.lower_node_id(e.id), - node: loop_expr, + kind, span: e.span, attrs: ThinVec::new(), }); // `mut iter => { ... }` - let iter_arm = self.arm(hir_vec![iter_pat], loop_expr); + let iter_arm = self.arm(iter_pat, loop_expr); // `match ::std::iter::IntoIterator::into_iter(<head>) { ... }` let into_iter_expr = { let into_iter_path = &[sym::iter, sym::IntoIterator, sym::into_iter]; P(self.expr_call_std_path( - head_sp, + desugared_span, into_iter_path, hir_vec![head], )) }; let match_expr = P(self.expr_match( - head_sp, + desugared_span, into_iter_expr, hir_vec![iter_arm], hir::MatchSource::ForLoopDesugar, @@ -1184,7 +1204,7 @@ // surrounding scope of the `match` since the `match` is not a terminating scope. // // Also, add the attributes to the outer returned expr node. - self.expr_drop_temps(head_sp, match_expr, e.attrs.clone()) + self.expr_drop_temps(desugared_span, match_expr, e.attrs.clone()) } /// Desugar `ExprKind::Try` from: `<expr>?` into: @@ -1244,7 +1264,7 @@ ThinVec::from(attrs.clone()), )); let ok_pat = self.pat_ok(span, val_pat); - self.arm(hir_vec![ok_pat], val_expr) + self.arm(ok_pat, val_expr) }; // `Err(err) => #[allow(unreachable_code)] @@ -1258,7 +1278,7 @@ self.expr_call_std_path(try_span, from_path, hir_vec![err_expr]) }; let from_err_expr = - self.wrap_in_try_constructor(sym::from_error, from_expr, unstable_span); + self.wrap_in_try_constructor(sym::from_error, unstable_span, from_expr, try_span); let thin_attrs = ThinVec::from(attrs); let catch_scope = self.catch_scopes.last().map(|x| *x); let ret_expr = if let Some(catch_node) = catch_scope { @@ -1279,7 +1299,7 @@ }; let err_pat = self.pat_err(try_span, err_local); - self.arm(hir_vec![err_pat], ret_expr) + self.arm(err_pat, ret_expr) }; hir::ExprKind::Match( @@ -1453,15 +1473,10 @@ pub(super) fn expr( &mut self, span: Span, - node: hir::ExprKind, + kind: hir::ExprKind, attrs: ThinVec<Attribute> ) -> hir::Expr { - hir::Expr { - hir_id: self.next_id(), - node, - span, - attrs, - } + hir::Expr { hir_id: self.next_id(), kind, span, attrs } } fn field(&mut self, ident: Ident, expr: P<hir::Expr>, span: Span) -> hir::Field { @@ -1474,14 +1489,11 @@ } } - /// HACK(or_patterns; Centril | dlrobertson): For now we don't push down top level or-patterns - /// `p | q` into `hir::PatKind::Or(...)` as post-lowering bits of the compiler are not ready - /// to deal with it. This should by fixed by pushing it down to HIR and then HAIR. - fn arm(&mut self, pats: HirVec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm { + fn arm(&mut self, pat: P<hir::Pat>, expr: P<hir::Expr>) -> hir::Arm { hir::Arm { hir_id: self.next_id(), attrs: hir_vec![], - pats, + pat, guard: None, span: expr.span, body: expr,
diff --git a/src/librustc/hir/lowering/item.rs b/src/librustc/hir/lowering/item.rs index 61be40a..9da8709 100644 --- a/src/librustc/hir/lowering/item.rs +++ b/src/librustc/hir/lowering/item.rs
@@ -18,7 +18,7 @@ use syntax::attr; use syntax::ast::*; use syntax::visit::{self, Visitor}; -use syntax::ext::base::SpecialDerives; +use syntax::expand::SpecialDerives; use syntax::source_map::{respan, DesugaringKind, Spanned}; use syntax::symbol::{kw, sym}; use syntax_pos::Span; @@ -45,14 +45,16 @@ impl<'tcx, 'interner> Visitor<'tcx> for ItemLowerer<'tcx, 'interner> { fn visit_mod(&mut self, m: &'tcx Mod, _s: Span, _attrs: &[Attribute], n: NodeId) { - self.lctx.modules.insert(n, hir::ModuleItems { + let hir_id = self.lctx.lower_node_id(n); + + self.lctx.modules.insert(hir_id, hir::ModuleItems { items: BTreeSet::new(), trait_items: BTreeSet::new(), impl_items: BTreeSet::new(), }); let old = self.lctx.current_module; - self.lctx.current_module = n; + self.lctx.current_module = hir_id; visit::walk_mod(self, m); self.lctx.current_module = old; } @@ -71,7 +73,7 @@ if let Some(hir_id) = item_hir_id { self.lctx.with_parent_item_lifetime_defs(hir_id, |this| { let this = &mut ItemLowerer { lctx: this }; - if let ItemKind::Impl(.., ref opt_trait_ref, _, _) = item.node { + if let ItemKind::Impl(.., ref opt_trait_ref, _, _) = item.kind { this.with_trait_impl_ref(opt_trait_ref, |this| { visit::walk_item(this, item) }); @@ -117,7 +119,7 @@ ) -> T { let old_len = self.in_scope_lifetimes.len(); - let parent_generics = match self.items.get(&parent_hir_id).unwrap().node { + let parent_generics = match self.items.get(&parent_hir_id).unwrap().kind { hir::ItemKind::Impl(_, _, _, ref generics, ..) | hir::ItemKind::Trait(_, _, ref generics, ..) => { &generics.params[..] @@ -166,7 +168,7 @@ } pub(super) fn lower_item_id(&mut self, i: &Item) -> SmallVec<[hir::ItemId; 1]> { - let node_ids = match i.node { + let node_ids = match i.kind { ItemKind::Use(ref use_tree) => { let mut vec = smallvec![i.id]; self.lower_item_id_use_tree(use_tree, i.id, &mut vec); @@ -233,7 +235,7 @@ } let attrs = attrs.into(); - if let ItemKind::MacroDef(ref def) = i.node { + if let ItemKind::MacroDef(ref def) = i.kind { if !def.legacy || attr::contains_name(&i.attrs, sym::macro_export) { let body = self.lower_token_stream(def.stream()); let hir_id = self.lower_node_id(i.id); @@ -252,13 +254,13 @@ return None; } - let node = self.lower_item_kind(i.id, &mut ident, &attrs, &mut vis, &i.node); + let kind = self.lower_item_kind(i.id, &mut ident, &attrs, &mut vis, &i.kind); Some(hir::Item { hir_id: self.lower_node_id(i.id), ident, attrs, - node, + kind, vis, span: i.span, }) @@ -540,7 +542,7 @@ let res = this.lower_res(res); let path = this.lower_path_extra(res, &path, ParamMode::Explicit, None); - let item = hir::ItemKind::Use(P(path), hir::UseKind::Single); + let kind = hir::ItemKind::Use(P(path), hir::UseKind::Single); let vis = this.rebuild_vis(&vis); this.insert_item( @@ -548,7 +550,7 @@ hir_id: new_id, ident, attrs: attrs.into_iter().cloned().collect(), - node: item, + kind, vis, span, }, @@ -556,8 +558,7 @@ }); } - let path = - P(self.lower_path_extra(ret_res, &path, ParamMode::Explicit, None)); + let path = P(self.lower_path_extra(ret_res, &path, ParamMode::Explicit, None)); hir::ItemKind::Use(path, hir::UseKind::Single) } UseTreeKind::Glob => { @@ -621,7 +622,7 @@ let mut vis = this.rebuild_vis(&vis); let mut ident = *ident; - let item = this.lower_use_tree(use_tree, + let kind = this.lower_use_tree(use_tree, &prefix, id, &mut vis, @@ -633,7 +634,7 @@ hir_id: new_hir_id, ident, attrs: attrs.into_iter().cloned().collect(), - node: item, + kind, vis, span: use_tree.span, }, @@ -710,7 +711,7 @@ hir_id: self.lower_node_id(i.id), ident: i.ident, attrs: self.lower_attrs(&i.attrs), - node: match i.node { + kind: match i.kind { ForeignItemKind::Fn(ref fdec, ref generics) => { let (generics, (fn_dec, fn_args)) = self.add_in_band_defs( generics, @@ -787,7 +788,7 @@ } fn lower_struct_field(&mut self, (index, f): (usize, &StructField)) -> hir::StructField { - let ty = if let TyKind::Path(ref qself, ref path) = f.ty.node { + let ty = if let TyKind::Path(ref qself, ref path) = f.ty.kind { let t = self.lower_path_ty( &f.ty, qself, @@ -816,7 +817,7 @@ fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem { let trait_item_def_id = self.resolver.definitions().local_def_id(i.id); - let (generics, node) = match i.node { + let (generics, kind) = match i.kind { TraitItemKind::Const(ref ty, ref default) => ( self.lower_generics(&i.generics, ImplTraitContext::disallowed()), hir::TraitItemKind::Const( @@ -850,14 +851,14 @@ } TraitItemKind::Type(ref bounds, ref default) => { let generics = self.lower_generics(&i.generics, ImplTraitContext::disallowed()); - let node = hir::TraitItemKind::Type( + let kind = hir::TraitItemKind::Type( self.lower_param_bounds(bounds, ImplTraitContext::disallowed()), default .as_ref() .map(|x| self.lower_ty(x, ImplTraitContext::disallowed())), ); - (generics, node) + (generics, kind) }, TraitItemKind::Macro(..) => bug!("macro item shouldn't exist at this point"), }; @@ -867,13 +868,13 @@ ident: i.ident, attrs: self.lower_attrs(&i.attrs), generics, - node, + kind, span: i.span, } } fn lower_trait_item_ref(&mut self, i: &TraitItem) -> hir::TraitItemRef { - let (kind, has_default) = match i.node { + let (kind, has_default) = match i.kind { TraitItemKind::Const(_, ref default) => { (hir::AssocItemKind::Const, default.is_some()) } @@ -900,7 +901,7 @@ fn lower_impl_item(&mut self, i: &ImplItem) -> hir::ImplItem { let impl_item_def_id = self.resolver.definitions().local_def_id(i.id); - let (generics, node) = match i.node { + let (generics, kind) = match i.kind { ImplItemKind::Const(ref ty, ref expr) => ( self.lower_generics(&i.generics, ImplTraitContext::disallowed()), hir::ImplItemKind::Const( @@ -944,7 +945,7 @@ generics, vis: self.lower_visibility(&i.vis, None), defaultness: self.lower_defaultness(i.defaultness, true /* [1] */), - node, + kind, span: i.span, } @@ -958,7 +959,7 @@ span: i.span, vis: self.lower_visibility(&i.vis, Some(i.id)), defaultness: self.lower_defaultness(i.defaultness, true /* [1] */), - kind: match i.node { + kind: match i.kind { ImplItemKind::Const(..) => hir::AssocItemKind::Const, ImplItemKind::TyAlias(..) => hir::AssocItemKind::Type, ImplItemKind::OpaqueTy(..) => hir::AssocItemKind::OpaqueTy, @@ -1131,7 +1132,7 @@ // Check if this is a binding pattern, if so, we can optimize and avoid adding a // `let <pat> = __argN;` statement. In this case, we do not rename the parameter. - let (ident, is_simple_parameter) = match parameter.pat.node { + let (ident, is_simple_parameter) = match parameter.pat.kind { hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, _) => (ident, true), _ => { @@ -1221,7 +1222,11 @@ } let async_expr = this.make_async_expr( - CaptureBy::Value, closure_id, None, body.span, + CaptureBy::Value, + closure_id, + None, + body.span, + hir::AsyncGeneratorKind::Fn, |this| { // Create a block from the user's function body: let user_body = this.lower_block_expr(body); @@ -1341,7 +1346,7 @@ ); }; // Check if the where clause type is a plain type parameter. - match bound_pred.bounded_ty.node { + match bound_pred.bounded_ty.kind { TyKind::Path(None, ref path) if path.segments.len() == 1 && bound_pred.bound_generic_params.is_empty() =>
diff --git a/src/librustc/hir/map/blocks.rs b/src/librustc/hir/map/blocks.rs index 351f581..f670d5a 100644 --- a/src/librustc/hir/map/blocks.rs +++ b/src/librustc/hir/map/blocks.rs
@@ -37,19 +37,25 @@ impl MaybeFnLike for ast::Item { fn is_fn_like(&self) -> bool { - match self.node { ast::ItemKind::Fn(..) => true, _ => false, } + match self.kind { + ast::ItemKind::Fn(..) => true, + _ => false, + } } } impl MaybeFnLike for ast::ImplItem { fn is_fn_like(&self) -> bool { - match self.node { ast::ImplItemKind::Method(..) => true, _ => false, } + match self.kind { + ast::ImplItemKind::Method(..) => true, + _ => false, + } } } impl MaybeFnLike for ast::TraitItem { fn is_fn_like(&self) -> bool { - match self.node { + match self.kind { ast::TraitItemKind::Method(_, ast::TraitMethod::Provided(_)) => true, _ => false, } @@ -58,7 +64,7 @@ impl MaybeFnLike for ast::Expr { fn is_fn_like(&self) -> bool { - match self.node { + match self.kind { ast::ExprKind::Closure(..) => true, _ => false, } @@ -212,7 +218,7 @@ C: FnOnce(ClosureParts<'a>) -> A, { match self.node { - map::Node::Item(i) => match i.node { + map::Node::Item(i) => match i.kind { ast::ItemKind::Fn(ref decl, header, ref generics, block) => item_fn(ItemFnParts { id: i.hir_id, @@ -227,21 +233,21 @@ }), _ => bug!("item FnLikeNode that is not fn-like"), }, - map::Node::TraitItem(ti) => match ti.node { + map::Node::TraitItem(ti) => match ti.kind { ast::TraitItemKind::Method(ref sig, ast::TraitMethod::Provided(body)) => { method(ti.hir_id, ti.ident, sig, None, body, ti.span, &ti.attrs) } _ => bug!("trait method FnLikeNode that is not fn-like"), }, map::Node::ImplItem(ii) => { - match ii.node { + match ii.kind { ast::ImplItemKind::Method(ref sig, body) => { method(ii.hir_id, ii.ident, sig, Some(&ii.vis), body, ii.span, &ii.attrs) } _ => bug!("impl method FnLikeNode that is not fn-like") } }, - map::Node::Expr(e) => match e.node { + map::Node::Expr(e) => match e.kind { ast::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) => closure(ClosureParts::new(&decl, block, e.hir_id, e.span, &e.attrs)), _ => bug!("expr FnLikeNode that is not fn-like"),
diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs index 4179cf2..b0fa844 100644 --- a/src/librustc/hir/map/collector.rs +++ b/src/librustc/hir/map/collector.rs
@@ -5,7 +5,7 @@ use crate::hir::def_id::{LOCAL_CRATE, CrateNum}; use crate::hir::intravisit::{Visitor, NestedVisitorMap}; use rustc_data_structures::svh::Svh; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc_index::vec::IndexVec; use crate::ich::Fingerprint; use crate::middle::cstore::CrateStore; use crate::session::CrateDisambiguator; @@ -17,7 +17,7 @@ use std::iter::repeat; use crate::ich::StableHashingContext; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; /// A visitor that walks over the HIR and collects `Node`s into a HIR map. pub(super) struct NodeCollector<'a, 'hir> { @@ -149,7 +149,7 @@ let mut collector = NodeCollector { krate, source_map: sess.source_map(), - map: vec![None; definitions.def_index_count()], + map: IndexVec::from_elem_n(IndexVec::new(), definitions.def_index_count()), parent_node: hir::CRATE_HIR_ID, current_signature_dep_index: root_mod_sig_dep_index, current_full_dep_index: root_mod_full_dep_index, @@ -186,13 +186,13 @@ }); let mut upstream_crates: Vec<_> = cstore.crates_untracked().iter().map(|&cnum| { - let name = cstore.crate_name_untracked(cnum).as_interned_str(); + let name = cstore.crate_name_untracked(cnum); let disambiguator = cstore.crate_disambiguator_untracked(cnum).to_fingerprint(); let hash = cstore.crate_hash_untracked(cnum); (name, disambiguator, hash) }).collect(); - upstream_crates.sort_unstable_by_key(|&(name, dis, _)| (name, dis)); + upstream_crates.sort_unstable_by_key(|&(name, dis, _)| (name.as_str(), dis)); // We hash the final, remapped names of all local source files so we // don't have to include the path prefix remapping commandline args. @@ -227,12 +227,8 @@ fn insert_entry(&mut self, id: HirId, entry: Entry<'hir>) { debug!("hir_map: {:?} => {:?}", id, entry); - let local_map = &mut self.map[id.owner.index()]; + let local_map = &mut self.map[id.owner]; let i = id.local_id.as_u32() as usize; - if local_map.is_none() { - *local_map = Some(IndexVec::with_capacity(i + 1)); - } - let local_map = local_map.as_mut().unwrap(); let len = local_map.len(); if i >= len { local_map.extend(repeat(None).take(i - len + 1)); @@ -378,7 +374,7 @@ self.with_dep_node_owner(i.hir_id.owner, i, |this| { this.insert(i.span, i.hir_id, Node::Item(i)); this.with_parent(i.hir_id, |this| { - if let ItemKind::Struct(ref struct_def, _) = i.node { + if let ItemKind::Struct(ref struct_def, _) = i.kind { // If this is a tuple or unit-like struct, register the constructor. if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def)); @@ -427,7 +423,7 @@ } fn visit_pat(&mut self, pat: &'hir Pat) { - let node = if let PatKind::Binding(..) = pat.node { + let node = if let PatKind::Binding(..) = pat.kind { Node::Binding(pat) } else { Node::Pat(pat) @@ -602,9 +598,7 @@ where T: HashStable<StableHashingContext<'hir>>, { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'hir>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'hir>, hasher: &mut StableHasher) { hcx.while_hashing_hir_bodies(self.hash_bodies, |hcx| { self.item_like.hash_stable(hcx, hasher); });
diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index d1cc7a8..70dc224 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs
@@ -2,10 +2,10 @@ use crate::hir::def_id::DefIndex; use syntax::ast::*; -use syntax::ext::hygiene::ExpnId; use syntax::visit; use syntax::symbol::{kw, sym}; use syntax::parse::token::{self, Token}; +use syntax_pos::hygiene::ExpnId; use syntax_pos::Span; /// Creates `DefId`s for nodes in the AST. @@ -57,7 +57,7 @@ // For async functions, we need to create their inner defs inside of a // closure to match their desugared representation. - let fn_def_data = DefPathData::ValueNs(name.as_interned_str()); + let fn_def_data = DefPathData::ValueNs(name); let fn_def = self.create_def(id, fn_def_data, span); return self.with_parent(fn_def, |this| { this.create_def(return_impl_trait_id, DefPathData::ImplTrait, span); @@ -75,22 +75,22 @@ } fn collect_field(&mut self, field: &'a StructField, index: Option<usize>) { + let index = |this: &Self| index.unwrap_or_else(|| { + let node_id = NodeId::placeholder_from_expn_id(this.expansion); + this.definitions.placeholder_field_index(node_id) + }); + if field.is_placeholder { + self.definitions.set_placeholder_field_index(field.id, index(self)); self.visit_macro_invoc(field.id); } else { - let name = field.ident.map(|ident| ident.name) - .or_else(|| index.map(sym::integer)) - .unwrap_or_else(|| { - let node_id = NodeId::placeholder_from_expn_id(self.expansion); - sym::integer(self.definitions.placeholder_field_indices[&node_id]) - }) - .as_interned_str(); + let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name); let def = self.create_def(field.id, DefPathData::ValueNs(name), field.span); self.with_parent(def, |this| visit::walk_struct_field(this, field)); } } - pub fn visit_macro_invoc(&mut self, id: NodeId) { + fn visit_macro_invoc(&mut self, id: NodeId) { self.definitions.set_invocation_parent(id.placeholder_to_expn_id(), self.parent_def); } } @@ -101,7 +101,7 @@ // Pick the def data. This need not be unique, but the more // information we encapsulate into, the better - let def_data = match i.node { + let def_data = match i.kind { ItemKind::Impl(..) => DefPathData::Impl, ItemKind::Mod(..) if i.ident.name == kw::Invalid => { return visit::walk_item(self, i); @@ -109,7 +109,7 @@ ItemKind::Mod(..) | ItemKind::Trait(..) | ItemKind::TraitAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) | ItemKind::OpaqueTy(..) | ItemKind::ExternCrate(..) | ItemKind::ForeignMod(..) | - ItemKind::TyAlias(..) => DefPathData::TypeNs(i.ident.as_interned_str()), + ItemKind::TyAlias(..) => DefPathData::TypeNs(i.ident.name), ItemKind::Fn( ref decl, ref header, @@ -127,8 +127,8 @@ ) } ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) => - DefPathData::ValueNs(i.ident.as_interned_str()), - ItemKind::MacroDef(..) => DefPathData::MacroNs(i.ident.as_interned_str()), + DefPathData::ValueNs(i.ident.name), + ItemKind::MacroDef(..) => DefPathData::MacroNs(i.ident.name), ItemKind::Mac(..) => return self.visit_macro_invoc(i.id), ItemKind::GlobalAsm(..) => DefPathData::Misc, ItemKind::Use(..) => { @@ -138,7 +138,7 @@ let def = self.create_def(i.id, def_data, i.span); self.with_parent(def, |this| { - match i.node { + match i.kind { ItemKind::Struct(ref struct_def, _) | ItemKind::Union(ref struct_def, _) => { // If this is a unit or tuple-like struct, register the constructor. if let Some(ctor_hir_id) = struct_def.ctor_id() { @@ -157,12 +157,12 @@ } fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) { - if let ForeignItemKind::Macro(_) = foreign_item.node { + if let ForeignItemKind::Macro(_) = foreign_item.kind { return self.visit_macro_invoc(foreign_item.id); } let def = self.create_def(foreign_item.id, - DefPathData::ValueNs(foreign_item.ident.as_interned_str()), + DefPathData::ValueNs(foreign_item.ident.name), foreign_item.span); self.with_parent(def, |this| { @@ -175,7 +175,7 @@ return self.visit_macro_invoc(v.id); } let def = self.create_def(v.id, - DefPathData::TypeNs(v.ident.as_interned_str()), + DefPathData::TypeNs(v.ident.name), v.span); self.with_parent(def, |this| { if let Some(ctor_hir_id) = v.data.ctor_id() { @@ -191,9 +191,6 @@ // and every such attribute expands into a single field after it's resolved. for (index, field) in data.fields().iter().enumerate() { self.collect_field(field, Some(index)); - if field.is_placeholder && field.ident.is_none() { - self.definitions.placeholder_field_indices.insert(field.id, index); - } } } @@ -202,7 +199,7 @@ self.visit_macro_invoc(param.id); return; } - let name = param.ident.as_interned_str(); + let name = param.ident.name; let def_path_data = match param.kind { GenericParamKind::Lifetime { .. } => DefPathData::LifetimeNs(name), GenericParamKind::Type { .. } => DefPathData::TypeNs(name), @@ -214,11 +211,11 @@ } fn visit_trait_item(&mut self, ti: &'a TraitItem) { - let def_data = match ti.node { + let def_data = match ti.kind { TraitItemKind::Method(..) | TraitItemKind::Const(..) => - DefPathData::ValueNs(ti.ident.as_interned_str()), + DefPathData::ValueNs(ti.ident.name), TraitItemKind::Type(..) => { - DefPathData::TypeNs(ti.ident.as_interned_str()) + DefPathData::TypeNs(ti.ident.name) }, TraitItemKind::Macro(..) => return self.visit_macro_invoc(ti.id), }; @@ -228,7 +225,7 @@ } fn visit_impl_item(&mut self, ii: &'a ImplItem) { - let def_data = match ii.node { + let def_data = match ii.kind { ImplItemKind::Method(MethodSig { ref header, ref decl, @@ -243,12 +240,10 @@ body, ) } - ImplItemKind::Method(..) | ImplItemKind::Const(..) => - DefPathData::ValueNs(ii.ident.as_interned_str()), + ImplItemKind::Method(..) | + ImplItemKind::Const(..) => DefPathData::ValueNs(ii.ident.name), ImplItemKind::TyAlias(..) | - ImplItemKind::OpaqueTy(..) => { - DefPathData::TypeNs(ii.ident.as_interned_str()) - }, + ImplItemKind::OpaqueTy(..) => DefPathData::TypeNs(ii.ident.name), ImplItemKind::Macro(..) => return self.visit_macro_invoc(ii.id), }; @@ -257,7 +252,7 @@ } fn visit_pat(&mut self, pat: &'a Pat) { - match pat.node { + match pat.kind { PatKind::Mac(..) => return self.visit_macro_invoc(pat.id), _ => visit::walk_pat(self, pat), } @@ -271,7 +266,7 @@ } fn visit_expr(&mut self, expr: &'a Expr) { - let parent_def = match expr.node { + let parent_def = match expr.kind { ExprKind::Mac(..) => return self.visit_macro_invoc(expr.id), ExprKind::Closure(_, asyncness, ..) => { // Async closures desugar to closures inside of closures, so @@ -292,7 +287,7 @@ } fn visit_ty(&mut self, ty: &'a Ty) { - match ty.node { + match ty.kind { TyKind::Mac(..) => return self.visit_macro_invoc(ty.id), TyKind::ImplTrait(node_id, _) => { self.create_def(node_id, DefPathData::ImplTrait, ty.span); @@ -303,7 +298,7 @@ } fn visit_stmt(&mut self, stmt: &'a Stmt) { - match stmt.node { + match stmt.kind { StmtKind::Mac(..) => self.visit_macro_invoc(stmt.id), _ => visit::walk_stmt(self, stmt), } @@ -312,7 +307,7 @@ fn visit_token(&mut self, t: Token) { if let token::Interpolated(nt) = t.kind { if let token::NtExpr(ref expr) = *nt { - if let ExprKind::Mac(..) = expr.node { + if let ExprKind::Mac(..) = expr.kind { self.visit_macro_invoc(expr.id); } }
diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs index 187bc59..450ab94 100644 --- a/src/librustc/hir/map/definitions.rs +++ b/src/librustc/hir/map/definitions.rs
@@ -11,14 +11,14 @@ use crate::util::nodemap::NodeMap; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::indexed_vec::{IndexVec}; +use rustc_index::vec::{IndexVec}; use rustc_data_structures::stable_hasher::StableHasher; use std::borrow::Borrow; use std::fmt::Write; use std::hash::Hash; use syntax::ast; -use syntax::ext::hygiene::ExpnId; -use syntax::symbol::{Symbol, sym, InternedString}; +use syntax_pos::symbol::{Symbol, sym}; +use syntax_pos::hygiene::ExpnId; use syntax_pos::{Span, DUMMY_SP}; /// The `DefPathTable` maps `DefIndex`es to `DefKey`s and vice versa. @@ -27,8 +27,8 @@ /// There is one `DefPathTable` for each crate. #[derive(Clone, Default, RustcDecodable, RustcEncodable)] pub struct DefPathTable { - index_to_key: Vec<DefKey>, - def_path_hashes: Vec<DefPathHash>, + index_to_key: IndexVec<DefIndex, DefKey>, + def_path_hashes: IndexVec<DefIndex, DefPathHash>, } impl DefPathTable { @@ -53,14 +53,14 @@ #[inline(always)] pub fn def_key(&self, index: DefIndex) -> DefKey { - self.index_to_key[index.index()].clone() + self.index_to_key[index] } #[inline(always)] pub fn def_path_hash(&self, index: DefIndex) -> DefPathHash { - let ret = self.def_path_hashes[index.index()]; - debug!("def_path_hash({:?}) = {:?}", index, ret); - return ret + let hash = self.def_path_hashes[index]; + debug!("def_path_hash({:?}) = {:?}", index, hash); + hash } pub fn add_def_path_hashes_to(&self, @@ -92,7 +92,7 @@ pub struct Definitions { table: DefPathTable, node_to_def_index: NodeMap<DefIndex>, - def_index_to_node: Vec<ast::NodeId>, + def_index_to_node: IndexVec<DefIndex, ast::NodeId>, pub(super) node_to_hir_id: IndexVec<ast::NodeId, hir::HirId>, /// If `ExpnId` is an ID of some macro expansion, /// then `DefId` is the normal module (`mod`) in which the expanded macro was defined. @@ -105,13 +105,13 @@ /// we know what parent node that fragment should be attached to thanks to this table. invocation_parents: FxHashMap<ExpnId, DefIndex>, /// Indices of unnamed struct or variant fields with unresolved attributes. - pub(super) placeholder_field_indices: NodeMap<usize>, + pub placeholder_field_indices: NodeMap<usize>, } /// A unique identifier that we can use to lookup a definition /// precisely. It combines the index of the definition's parent (if /// any) with a `DisambiguatedDefPathData`. -#[derive(Clone, PartialEq, Debug, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] pub struct DefKey { /// The parent path. pub parent: Option<DefIndex>, @@ -136,7 +136,9 @@ ::std::mem::discriminant(data).hash(&mut hasher); if let Some(name) = data.get_opt_name() { - name.hash(&mut hasher); + // Get a stable hash by considering the symbol chars rather than + // the symbol index. + name.as_str().hash(&mut hasher); } disambiguator.hash(&mut hasher); @@ -162,13 +164,13 @@ /// between them. This introduces some artificial ordering dependency /// but means that if you have, e.g., two impls for the same type in /// the same module, they do get distinct `DefId`s. -#[derive(Clone, PartialEq, Debug, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] pub struct DisambiguatedDefPathData { pub data: DefPathData, pub disambiguator: u32 } -#[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct DefPath { /// The path leading from the crate root to the item. pub data: Vec<DisambiguatedDefPathData>, @@ -218,7 +220,7 @@ for component in &self.data { write!(s, "::{}[{}]", - component.data.as_interned_str(), + component.data.as_symbol(), component.disambiguator) .unwrap(); } @@ -238,11 +240,11 @@ for component in &self.data { if component.disambiguator == 0 { - write!(s, "::{}", component.data.as_interned_str()).unwrap(); + write!(s, "::{}", component.data.as_symbol()).unwrap(); } else { write!(s, "{}[{}]", - component.data.as_interned_str(), + component.data.as_symbol(), component.disambiguator) .unwrap(); } @@ -262,11 +264,11 @@ opt_delimiter.map(|d| s.push(d)); opt_delimiter = Some('-'); if component.disambiguator == 0 { - write!(s, "{}", component.data.as_interned_str()).unwrap(); + write!(s, "{}", component.data.as_symbol()).unwrap(); } else { write!(s, "{}[{}]", - component.data.as_interned_str(), + component.data.as_symbol(), component.disambiguator) .unwrap(); } @@ -275,7 +277,7 @@ } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum DefPathData { // Root: these should only be used for the root nodes, because // they are treated specially by the `def_path` function. @@ -290,13 +292,13 @@ /// An impl. Impl, /// Something in the type namespace. - TypeNs(InternedString), + TypeNs(Symbol), /// Something in the value namespace. - ValueNs(InternedString), + ValueNs(Symbol), /// Something in the macro namespace. - MacroNs(InternedString), + MacroNs(Symbol), /// Something in the lifetime namespace. - LifetimeNs(InternedString), + LifetimeNs(Symbol), /// A closure expression. ClosureExpr, @@ -311,7 +313,7 @@ /// Identifies a piece of crate metadata that is global to a whole crate /// (as opposed to just one item). `GlobalMetaData` components are only /// supposed to show up right below the crate root. - GlobalMetaData(InternedString), + GlobalMetaData(Symbol), } #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, @@ -357,7 +359,7 @@ #[inline] pub fn opt_def_index(&self, node: ast::NodeId) -> Option<DefIndex> { - self.node_to_def_index.get(&node).cloned() + self.node_to_def_index.get(&node).copied() } #[inline] @@ -373,7 +375,7 @@ #[inline] pub fn as_local_node_id(&self, def_id: DefId) -> Option<ast::NodeId> { if def_id.krate == LOCAL_CRATE { - let node_id = self.def_index_to_node[def_id.index.index()]; + let node_id = self.def_index_to_node[def_id.index]; if node_id != ast::DUMMY_NODE_ID { return Some(node_id); } @@ -402,7 +404,7 @@ #[inline] pub fn def_index_to_hir_id(&self, def_index: DefIndex) -> hir::HirId { - let node_id = self.def_index_to_node[def_index.index()]; + let node_id = self.def_index_to_node[def_index]; self.node_to_hir_id[node_id] } @@ -411,7 +413,7 @@ #[inline] pub fn opt_span(&self, def_id: DefId) -> Option<Span> { if def_id.krate == LOCAL_CRATE { - self.def_index_to_span.get(&def_id.index).cloned() + self.def_index_to_span.get(&def_id.index).copied() } else { None } @@ -470,7 +472,7 @@ // Find the next free disambiguator for this key. let disambiguator = { - let next_disamb = self.next_disambiguator.entry((parent, data.clone())).or_insert(0); + let next_disamb = self.next_disambiguator.entry((parent, data)).or_insert(0); let disambiguator = *next_disamb; *next_disamb = next_disamb.checked_add(1).expect("disambiguator overflow"); disambiguator @@ -523,7 +525,7 @@ } pub fn expansion_that_defined(&self, index: DefIndex) -> ExpnId { - self.expansions_that_defined.get(&index).cloned().unwrap_or(ExpnId::root()) + self.expansions_that_defined.get(&index).copied().unwrap_or(ExpnId::root()) } pub fn parent_module_of_macro_def(&self, expn_id: ExpnId) -> DefId { @@ -542,10 +544,19 @@ let old_parent = self.invocation_parents.insert(invoc_id, parent); assert!(old_parent.is_none(), "parent `DefIndex` is reset for an invocation"); } + + pub fn placeholder_field_index(&self, node_id: ast::NodeId) -> usize { + self.placeholder_field_indices[&node_id] + } + + pub fn set_placeholder_field_index(&mut self, node_id: ast::NodeId, index: usize) { + let old_index = self.placeholder_field_indices.insert(node_id, index); + assert!(old_index.is_none(), "placeholder field index is reset for a node ID"); + } } impl DefPathData { - pub fn get_opt_name(&self) -> Option<InternedString> { + pub fn get_opt_name(&self) -> Option<Symbol> { use self::DefPathData::*; match *self { TypeNs(name) | @@ -564,15 +575,15 @@ } } - pub fn as_interned_str(&self) -> InternedString { + pub fn as_symbol(&self) -> Symbol { use self::DefPathData::*; - let s = match *self { + match *self { TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) | GlobalMetaData(name) => { - return name + name } // Note that this does not show up in user print-outs. CrateRoot => sym::double_braced_crate, @@ -582,13 +593,11 @@ Ctor => sym::double_braced_constructor, AnonConst => sym::double_braced_constant, ImplTrait => sym::double_braced_opaque, - }; - - s.as_interned_str() + } } pub fn to_string(&self) -> String { - self.as_interned_str().to_string() + self.as_symbol().to_string() } } @@ -599,7 +608,6 @@ (pub enum GlobalMetaDataKind { $($variant:ident),* }) => ( - #[derive(Clone, Copy, Debug, Hash, RustcEncodable, RustcDecodable)] pub enum GlobalMetaDataKind { $($variant),* } @@ -611,7 +619,7 @@ definitions.create_def_with_parent( CRATE_DEF_INDEX, ast::DUMMY_NODE_ID, - DefPathData::GlobalMetaData(instance.name().as_interned_str()), + DefPathData::GlobalMetaData(instance.name()), ExpnId::root(), DUMMY_SP ); @@ -625,7 +633,7 @@ let def_key = DefKey { parent: Some(CRATE_DEF_INDEX), disambiguated_data: DisambiguatedDefPathData { - data: DefPathData::GlobalMetaData(self.name().as_interned_str()), + data: DefPathData::GlobalMetaData(self.name()), disambiguator: 0, } };
diff --git a/src/librustc/hir/map/hir_id_validator.rs b/src/librustc/hir/map/hir_id_validator.rs index 8896593..b66c2ce 100644 --- a/src/librustc/hir/map/hir_id_validator.rs +++ b/src/librustc/hir/map/hir_id_validator.rs
@@ -10,7 +10,7 @@ let errors = Lock::new(Vec::new()); par_iter(&hir_map.krate().modules).for_each(|(module_id, _)| { - let local_def_id = hir_map.local_def_id_from_node_id(*module_id); + let local_def_id = hir_map.local_def_id(*module_id); hir_map.visit_item_likes_in_module(local_def_id, &mut OuterVisitor { hir_map, errors: &errors,
diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 5cec8a5..acadd77 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs
@@ -17,14 +17,12 @@ use rustc_target::spec::abi::Abi; use rustc_data_structures::svh::Svh; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc_index::vec::IndexVec; use syntax::ast::{self, Name, NodeId}; use syntax::source_map::Spanned; -use syntax::ext::base::MacroKind; +use syntax_pos::hygiene::MacroKind; use syntax_pos::{Span, DUMMY_SP}; -use std::result::Result::Err; - pub mod blocks; mod collector; mod def_collector; @@ -50,28 +48,28 @@ fn fn_decl(&self) -> Option<&'hir FnDecl> { match self.node { Node::Item(ref item) => { - match item.node { + match item.kind { ItemKind::Fn(ref fn_decl, _, _, _) => Some(fn_decl), _ => None, } } Node::TraitItem(ref item) => { - match item.node { + match item.kind { TraitItemKind::Method(ref method_sig, _) => Some(&method_sig.decl), _ => None } } Node::ImplItem(ref item) => { - match item.node { + match item.kind { ImplItemKind::Method(ref method_sig, _) => Some(&method_sig.decl), _ => None, } } Node::Expr(ref expr) => { - match expr.node { + match expr.kind { ExprKind::Closure(_, ref fn_decl, ..) => Some(fn_decl), _ => None, } @@ -84,7 +82,7 @@ fn associated_body(self) -> Option<BodyId> { match self.node { Node::Item(item) => { - match item.node { + match item.kind { ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(_, _, _, body) => Some(body), @@ -93,7 +91,7 @@ } Node::TraitItem(item) => { - match item.node { + match item.kind { TraitItemKind::Const(_, Some(body)) | TraitItemKind::Method(_, TraitMethod::Provided(body)) => Some(body), _ => None @@ -101,7 +99,7 @@ } Node::ImplItem(item) => { - match item.node { + match item.kind { ImplItemKind::Const(_, body) | ImplItemKind::Method(_, body) => Some(body), _ => None, @@ -111,7 +109,7 @@ Node::AnonConst(constant) => Some(constant.body), Node::Expr(expr) => { - match expr.node { + match expr.kind { ExprKind::Closure(.., body, _, _) => Some(body), _ => None, } @@ -158,9 +156,9 @@ /// This type is effectively a `HashMap<HirId, Entry<'hir>>`, /// but it is implemented as 2 layers of arrays. -/// - first we have `A = Vec<Option<B>>` mapping a `DefIndex`'s index to an inner value +/// - first we have `A = IndexVec<DefIndex, B>` mapping `DefIndex`s to an inner value /// - which is `B = IndexVec<ItemLocalId, Option<Entry<'hir>>` which gives you the `Entry`. -pub(super) type HirEntryMap<'hir> = Vec<Option<IndexVec<ItemLocalId, Option<Entry<'hir>>>>>; +pub(super) type HirEntryMap<'hir> = IndexVec<DefIndex, IndexVec<ItemLocalId, Option<Entry<'hir>>>>; /// Represents a mapping from `NodeId`s to AST elements and their parent `NodeId`s. #[derive(Clone)] @@ -183,11 +181,49 @@ hir_to_node_id: FxHashMap<HirId, NodeId>, } +struct ParentHirIterator<'map> { + current_id: HirId, + map: &'map Map<'map>, +} + +impl<'map> ParentHirIterator<'map> { + fn new(current_id: HirId, map: &'map Map<'map>) -> ParentHirIterator<'map> { + ParentHirIterator { + current_id, + map, + } + } +} + +impl<'map> Iterator for ParentHirIterator<'map> { + type Item = (HirId, Node<'map>); + + fn next(&mut self) -> Option<Self::Item> { + if self.current_id == CRATE_HIR_ID { + return None; + } + loop { // There are nodes that do not have entries, so we need to skip them. + let parent_id = self.map.get_parent_node(self.current_id); + + if parent_id == self.current_id { + self.current_id = CRATE_HIR_ID; + return None; + } + + self.current_id = parent_id; + if let Some(entry) = self.map.find_entry(parent_id) { + return Some((parent_id, entry.node)); + } + // If this `HirId` doesn't have an `Entry`, skip it and look for its `parent_id`. + } + } +} + impl<'hir> Map<'hir> { #[inline] fn lookup(&self, id: HirId) -> Option<&Entry<'hir>> { - let local_map = self.map.get(id.owner.index())?; - local_map.as_ref()?.get(id.local_id)?.as_ref() + let local_map = self.map.get(id.owner)?; + local_map.get(id.local_id)?.as_ref() } /// Registers a read in the dependency graph of the AST node with @@ -293,7 +329,7 @@ Some(match node { Node::Item(item) => { - match item.node { + match item.kind { ItemKind::Static(..) => DefKind::Static, ItemKind::Const(..) => DefKind::Const, ItemKind::Fn(..) => DefKind::Fn, @@ -313,21 +349,21 @@ } } Node::ForeignItem(item) => { - match item.node { + match item.kind { ForeignItemKind::Fn(..) => DefKind::Fn, ForeignItemKind::Static(..) => DefKind::Static, ForeignItemKind::Type => DefKind::ForeignTy, } } Node::TraitItem(item) => { - match item.node { + match item.kind { TraitItemKind::Const(..) => DefKind::AssocConst, TraitItemKind::Method(..) => DefKind::Method, TraitItemKind::Type(..) => DefKind::AssocTy, } } Node::ImplItem(item) => { - match item.node { + match item.kind { ImplItemKind::Const(..) => DefKind::AssocConst, ImplItemKind::Method(..) => DefKind::Method, ImplItemKind::TyAlias(..) => DefKind::AssocTy, @@ -453,22 +489,22 @@ pub fn body_owner_kind(&self, id: HirId) -> BodyOwnerKind { match self.get(id) { - Node::Item(&Item { node: ItemKind::Const(..), .. }) | - Node::TraitItem(&TraitItem { node: TraitItemKind::Const(..), .. }) | - Node::ImplItem(&ImplItem { node: ImplItemKind::Const(..), .. }) | + Node::Item(&Item { kind: ItemKind::Const(..), .. }) | + Node::TraitItem(&TraitItem { kind: TraitItemKind::Const(..), .. }) | + Node::ImplItem(&ImplItem { kind: ImplItemKind::Const(..), .. }) | Node::AnonConst(_) => { BodyOwnerKind::Const } Node::Ctor(..) | - Node::Item(&Item { node: ItemKind::Fn(..), .. }) | - Node::TraitItem(&TraitItem { node: TraitItemKind::Method(..), .. }) | - Node::ImplItem(&ImplItem { node: ImplItemKind::Method(..), .. }) => { + Node::Item(&Item { kind: ItemKind::Fn(..), .. }) | + Node::TraitItem(&TraitItem { kind: TraitItemKind::Method(..), .. }) | + Node::ImplItem(&ImplItem { kind: ImplItemKind::Method(..), .. }) => { BodyOwnerKind::Fn } - Node::Item(&Item { node: ItemKind::Static(_, m, _), .. }) => { + Node::Item(&Item { kind: ItemKind::Static(_, m, _), .. }) => { BodyOwnerKind::Static(m) } - Node::Expr(&Expr { node: ExprKind::Closure(..), .. }) => { + Node::Expr(&Expr { kind: ExprKind::Closure(..), .. }) => { BodyOwnerKind::Closure } node => bug!("{:#?} is not a body node", node), @@ -477,8 +513,8 @@ pub fn ty_param_owner(&self, id: HirId) -> HirId { match self.get(id) { - Node::Item(&Item { node: ItemKind::Trait(..), .. }) | - Node::Item(&Item { node: ItemKind::TraitAlias(..), .. }) => id, + Node::Item(&Item { kind: ItemKind::Trait(..), .. }) | + Node::Item(&Item { kind: ItemKind::TraitAlias(..), .. }) => id, Node::GenericParam(_) => self.get_parent_node(id), _ => bug!("ty_param_owner: {} not a type parameter", self.node_to_string(id)) } @@ -486,8 +522,8 @@ pub fn ty_param_name(&self, id: HirId) -> Name { match self.get(id) { - Node::Item(&Item { node: ItemKind::Trait(..), .. }) | - Node::Item(&Item { node: ItemKind::TraitAlias(..), .. }) => kw::SelfUpper, + Node::Item(&Item { kind: ItemKind::Trait(..), .. }) | + Node::Item(&Item { kind: ItemKind::TraitAlias(..), .. }) => kw::SelfUpper, Node::GenericParam(param) => param.name.ident().name, _ => bug!("ty_param_name: {} not a type parameter", self.node_to_string(id)), } @@ -517,7 +553,7 @@ match self.find_entry(hir_id).unwrap().node { Node::Item(&Item { span, - node: ItemKind::Mod(ref m), + kind: ItemKind::Mod(ref m), .. }) => (m, span, hir_id), Node::Crate => (&self.forest.krate.module, self.forest.krate.span, hir_id), @@ -536,9 +572,7 @@ // in the expect_* calls the loops below self.read(hir_id); - let node_id = self.hir_to_node_id[&hir_id]; - - let module = &self.forest.krate.modules[&node_id]; + let module = &self.forest.krate.modules[&hir_id]; for id in &module.items { visitor.visit_item(self.expect_item(*id)); @@ -570,7 +604,7 @@ Node::ImplItem(ref impl_item) => Some(&impl_item.generics), Node::TraitItem(ref trait_item) => Some(&trait_item.generics), Node::Item(ref item) => { - match item.node { + match item.kind { ItemKind::Fn(_, _, ref generics, _) | ItemKind::TyAlias(_, ref generics) | ItemKind::Enum(_, ref generics) | @@ -636,7 +670,7 @@ Some(Node::TraitItem(_)) | Some(Node::ImplItem(_)) => true, Some(Node::Expr(e)) => { - match e.node { + match e.kind { ExprKind::Closure(..) => true, _ => false, } @@ -651,24 +685,24 @@ let parent_id = self.get_parent_item(hir_id); match self.get(parent_id) { Node::Item(&Item { - node: ItemKind::Const(..), + kind: ItemKind::Const(..), .. }) | Node::TraitItem(&TraitItem { - node: TraitItemKind::Const(..), + kind: TraitItemKind::Const(..), .. }) | Node::ImplItem(&ImplItem { - node: ImplItemKind::Const(..), + kind: ImplItemKind::Const(..), .. }) | Node::AnonConst(_) | Node::Item(&Item { - node: ItemKind::Static(..), + kind: ItemKind::Static(..), .. }) => true, Node::Item(&Item { - node: ItemKind::Fn(_, header, ..), + kind: ItemKind::Fn(_, header, ..), .. }) => header.constness == Constness::Const, _ => false, @@ -678,51 +712,12 @@ /// Wether `hir_id` corresponds to a `mod` or a crate. pub fn is_hir_id_module(&self, hir_id: HirId) -> bool { match self.lookup(hir_id) { - Some(Entry { node: Node::Item(Item { node: ItemKind::Mod(_), .. }), .. }) | + Some(Entry { node: Node::Item(Item { kind: ItemKind::Mod(_), .. }), .. }) | Some(Entry { node: Node::Crate, .. }) => true, _ => false, } } - - /// If there is some error when walking the parents (e.g., a node does not - /// have a parent in the map or a node can't be found), then we return the - /// last good `HirId` we found. Note that reaching the crate root (`id == 0`), - /// is not an error, since items in the crate module have the crate root as - /// parent. - fn walk_parent_nodes<F, F2>(&self, - start_id: HirId, - found: F, - bail_early: F2) - -> Result<HirId, HirId> - where F: Fn(&Node<'hir>) -> bool, F2: Fn(&Node<'hir>) -> bool - { - let mut id = start_id; - loop { - let parent_id = self.get_parent_node(id); - if parent_id == CRATE_HIR_ID { - return Ok(CRATE_HIR_ID); - } - if parent_id == id { - return Err(id); - } - - if let Some(entry) = self.find_entry(parent_id) { - if let Node::Crate = entry.node { - return Err(id); - } - if found(&entry.node) { - return Ok(parent_id); - } else if bail_early(&entry.node) { - return Err(parent_id); - } - id = parent_id; - } else { - return Err(id); - } - } - } - /// Retrieves the `HirId` for `id`'s enclosing method, unless there's a /// `while` or `loop` before reaching it, as block tail returns are not /// available in them. @@ -746,29 +741,46 @@ /// } /// ``` pub fn get_return_block(&self, id: HirId) -> Option<HirId> { - let match_fn = |node: &Node<'_>| { - match *node { + let mut iter = ParentHirIterator::new(id, &self).peekable(); + let mut ignore_tail = false; + if let Some(entry) = self.find_entry(id) { + if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = entry.node { + // When dealing with `return` statements, we don't care about climbing only tail + // expressions. + ignore_tail = true; + } + } + while let Some((hir_id, node)) = iter.next() { + if let (Some((_, next_node)), false) = (iter.peek(), ignore_tail) { + match next_node { + Node::Block(Block { expr: None, .. }) => return None, + Node::Block(Block { expr: Some(expr), .. }) => { + if hir_id != expr.hir_id { + // The current node is not the tail expression of its parent. + return None; + } + } + _ => {} + } + } + match node { Node::Item(_) | Node::ForeignItem(_) | Node::TraitItem(_) | - Node::Expr(Expr { node: ExprKind::Closure(..), ..}) | - Node::ImplItem(_) => true, - _ => false, - } - }; - let match_non_returning_block = |node: &Node<'_>| { - match *node { + Node::Expr(Expr { kind: ExprKind::Closure(..), ..}) | + Node::ImplItem(_) => return Some(hir_id), Node::Expr(ref expr) => { - match expr.node { - ExprKind::Loop(..) | ExprKind::Ret(..) => true, - _ => false, + match expr.kind { + // Ignore `return`s on the first iteration + ExprKind::Loop(..) | ExprKind::Ret(..) => return None, + _ => {} } } - _ => false, + Node::Local(_) => return None, + _ => {} } - }; - - self.walk_parent_nodes(id, match_fn, match_non_returning_block).ok() + } + None } /// Retrieves the `HirId` for `id`'s parent item, or `id` itself if no @@ -776,16 +788,17 @@ /// in the HIR which is recorded by the map and is an item, either an item /// in a module, trait, or impl. pub fn get_parent_item(&self, hir_id: HirId) -> HirId { - match self.walk_parent_nodes(hir_id, |node| match *node { - Node::Item(_) | - Node::ForeignItem(_) | - Node::TraitItem(_) | - Node::ImplItem(_) => true, - _ => false, - }, |_| false) { - Ok(id) => id, - Err(id) => id, + for (hir_id, node) in ParentHirIterator::new(hir_id, &self) { + match node { + Node::Crate | + Node::Item(_) | + Node::ForeignItem(_) | + Node::TraitItem(_) | + Node::ImplItem(_) => return hir_id, + _ => {} + } } + hir_id } /// Returns the `DefId` of `id`'s nearest module parent, or `id` itself if no @@ -797,64 +810,94 @@ /// Returns the `HirId` of `id`'s nearest module parent, or `id` itself if no /// module parent is in this map. pub fn get_module_parent_node(&self, hir_id: HirId) -> HirId { - match self.walk_parent_nodes(hir_id, |node| match *node { - Node::Item(&Item { node: ItemKind::Mod(_), .. }) => true, - _ => false, - }, |_| false) { - Ok(id) => id, - Err(id) => id, + for (hir_id, node) in ParentHirIterator::new(hir_id, &self) { + if let Node::Item(&Item { kind: ItemKind::Mod(_), .. }) = node { + return hir_id; + } } + CRATE_HIR_ID + } + + /// When on a match arm tail expression or on a match arm, give back the enclosing `match` + /// expression. + /// + /// Used by error reporting when there's a type error in a match arm caused by the `match` + /// expression needing to be unit. + pub fn get_match_if_cause(&self, hir_id: HirId) -> Option<&Expr> { + for (_, node) in ParentHirIterator::new(hir_id, &self) { + match node { + Node::Item(_) | + Node::ForeignItem(_) | + Node::TraitItem(_) | + Node::ImplItem(_) => break, + Node::Expr(expr) => match expr.kind { + ExprKind::Match(_, _, _) => return Some(expr), + _ => {} + }, + Node::Stmt(stmt) => match stmt.kind { + StmtKind::Local(_) => break, + _ => {} + } + _ => {} + } + } + None } /// Returns the nearest enclosing scope. A scope is roughly an item or block. pub fn get_enclosing_scope(&self, hir_id: HirId) -> Option<HirId> { - self.walk_parent_nodes(hir_id, |node| match *node { - Node::Item(i) => { - match i.node { - ItemKind::Fn(..) - | ItemKind::Mod(..) - | ItemKind::Enum(..) - | ItemKind::Struct(..) - | ItemKind::Union(..) - | ItemKind::Trait(..) - | ItemKind::Impl(..) => true, - _ => false, - } - }, - Node::ForeignItem(fi) => { - match fi.node { - ForeignItemKind::Fn(..) => true, - _ => false, - } - }, - Node::TraitItem(ti) => { - match ti.node { - TraitItemKind::Method(..) => true, - _ => false, - } - }, - Node::ImplItem(ii) => { - match ii.node { - ImplItemKind::Method(..) => true, - _ => false, - } - }, - Node::Block(_) => true, - _ => false, - }, |_| false).ok() + for (hir_id, node) in ParentHirIterator::new(hir_id, &self) { + if match node { + Node::Item(i) => { + match i.kind { + ItemKind::Fn(..) + | ItemKind::Mod(..) + | ItemKind::Enum(..) + | ItemKind::Struct(..) + | ItemKind::Union(..) + | ItemKind::Trait(..) + | ItemKind::Impl(..) => true, + _ => false, + } + }, + Node::ForeignItem(fi) => { + match fi.kind { + ForeignItemKind::Fn(..) => true, + _ => false, + } + }, + Node::TraitItem(ti) => { + match ti.kind { + TraitItemKind::Method(..) => true, + _ => false, + } + }, + Node::ImplItem(ii) => { + match ii.kind { + ImplItemKind::Method(..) => true, + _ => false, + } + }, + Node::Block(_) => true, + _ => false, + } { + return Some(hir_id); + } + } + None } /// Returns the defining scope for an opaque type definition. - pub fn get_defining_scope(&self, id: HirId) -> Option<HirId> { + pub fn get_defining_scope(&self, id: HirId) -> HirId { let mut scope = id; loop { - scope = self.get_enclosing_scope(scope)?; + scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID); if scope == CRATE_HIR_ID { - return Some(CRATE_HIR_ID); + return CRATE_HIR_ID; } match self.get(scope) { Node::Item(i) => { - match i.node { + match i.kind { ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: None, .. }) => {} _ => break, } @@ -863,7 +906,7 @@ _ => break, } } - Some(scope) + scope } pub fn get_parent_did(&self, id: HirId) -> DefId { @@ -874,7 +917,7 @@ let parent = self.get_parent_item(hir_id); if let Some(entry) = self.find_entry(parent) { if let Entry { - node: Node::Item(Item { node: ItemKind::ForeignMod(ref nm), .. }), .. } = entry + node: Node::Item(Item { kind: ItemKind::ForeignMod(ref nm), .. }), .. } = entry { self.read(hir_id); // reveals some of the content of a node return nm.abi; @@ -907,7 +950,7 @@ pub fn expect_variant_data(&self, id: HirId) -> &'hir VariantData { match self.find(id) { Some(Node::Item(i)) => { - match i.node { + match i.kind { ItemKind::Struct(ref struct_def, _) | ItemKind::Union(ref struct_def, _) => struct_def, _ => bug!("struct ID bound to non-struct {}", self.node_to_string(id)) @@ -950,7 +993,7 @@ Node::Field(f) => f.ident.name, Node::Lifetime(lt) => lt.name.ident().name, Node::GenericParam(param) => param.name.ident().name, - Node::Binding(&Pat { node: PatKind::Binding(_, _, l, _), .. }) => l.name, + Node::Binding(&Pat { kind: PatKind::Binding(_, _, l, _), .. }) => l.name, Node::Ctor(..) => self.name(self.get_parent_item(id)), _ => bug!("no name for {}", self.node_to_string(id)) } @@ -970,7 +1013,7 @@ Some(Node::Variant(ref v)) => Some(&v.attrs[..]), Some(Node::Field(ref f)) => Some(&f.attrs[..]), Some(Node::Expr(ref e)) => Some(&*e.attrs), - Some(Node::Stmt(ref s)) => Some(s.node.attrs()), + Some(Node::Stmt(ref s)) => Some(s.kind.attrs()), Some(Node::Arm(ref a)) => Some(&*a.attrs), Some(Node::GenericParam(param)) => Some(¶m.attrs[..]), // Unit/tuple structs/variants take the attributes straight from @@ -988,14 +1031,12 @@ // see the comment on `HirEntryMap`. // Iterate over all the indices and return a reference to // local maps and their index given that they exist. - self.map.iter().enumerate().filter_map(|(i, local_map)| { - local_map.as_ref().map(|m| (i, m)) - }).flat_map(move |(array_index, local_map)| { + self.map.iter_enumerated().flat_map(move |(owner, local_map)| { // Iterate over each valid entry in the local map. local_map.iter_enumerated().filter_map(move |(i, entry)| entry.map(move |_| { // Reconstruct the `HirId` based on the 3 indices we used to find it. HirId { - owner: DefIndex::from(array_index), + owner, local_id: i, } })) @@ -1066,6 +1107,14 @@ self.as_local_hir_id(id).map(|id| self.span(id)) } + pub fn res_span(&self, res: Res) -> Option<Span> { + match res { + Res::Err => None, + Res::Local(id) => Some(self.span(id)), + res => self.span_if_local(res.opt_def_id()?), + } + } + pub fn node_to_string(&self, id: HirId) -> String { hir_id_to_string(self, id, true) } @@ -1125,7 +1174,7 @@ } fn item_is_mod(item: &Item) -> bool { - match item.node { + match item.kind { ItemKind::Mod(_) => true, _ => false, } @@ -1171,6 +1220,8 @@ forest: &'hir Forest, definitions: &'hir Definitions) -> Map<'hir> { + let _prof_timer = sess.prof.generic_activity("build_hir_map"); + // Build the reverse mapping of `node_to_hir_id`. let hir_to_node_id = definitions.node_to_hir_id.iter_enumerated() .map(|(node_id, &hir_id)| (hir_id, node_id)).collect(); @@ -1288,7 +1339,7 @@ match map.find(id) { Some(Node::Item(item)) => { - let item_str = match item.node { + let item_str = match item.kind { ItemKind::ExternCrate(..) => "extern crate", ItemKind::Use(..) => "use", ItemKind::Static(..) => "static", @@ -1312,7 +1363,7 @@ format!("foreign item {}{}", path_str(), id_str) } Some(Node::ImplItem(ii)) => { - match ii.node { + match ii.kind { ImplItemKind::Const(..) => { format!("assoc const {} in {}{}", ii.ident, path_str(), id_str) } @@ -1328,7 +1379,7 @@ } } Some(Node::TraitItem(ti)) => { - let kind = match ti.node { + let kind = match ti.kind { TraitItemKind::Const(..) => "assoc constant", TraitItemKind::Method(..) => "trait method", TraitItemKind::Type(..) => "assoc type",
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 92a8c00..0edc41e 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs
@@ -19,7 +19,7 @@ use crate::util::nodemap::{NodeMap, FxHashSet}; use errors::FatalError; -use syntax_pos::{Span, DUMMY_SP, symbol::InternedString, MultiSpan}; +use syntax_pos::{Span, DUMMY_SP, MultiSpan}; use syntax::source_map::Spanned; use syntax::ast::{self, CrateSugar, Ident, Name, NodeId, AsmDialect}; use syntax::ast::{Attribute, Label, LitKind, StrStyle, FloatTy, IntTy, UintTy}; @@ -122,9 +122,9 @@ // Hack to ensure that we don't try to access the private parts of `ItemLocalId` in this module. mod item_local_id_inner { - use rustc_data_structures::indexed_vec::Idx; + use rustc_index::vec::Idx; use rustc_macros::HashStable; - newtype_index! { + rustc_index::newtype_index! { /// An `ItemLocalId` uniquely identifies something within a given "item-like"; /// that is, within a `hir::Item`, `hir::TraitItem`, or `hir::ImplItem`. There is no /// guarantee that the numerical value of a given `ItemLocalId` corresponds to @@ -479,7 +479,7 @@ match arg { GenericArg::Lifetime(_) => {} GenericArg::Type(ref ty) => { - if let TyKind::Tup(ref tys) = ty.node { + if let TyKind::Tup(ref tys) = ty.kind { return tys; } break; @@ -628,9 +628,9 @@ own_counts } - pub fn get_named(&self, name: InternedString) -> Option<&GenericParam> { + pub fn get_named(&self, name: Symbol) -> Option<&GenericParam> { for param in &self.params { - if name == param.name.ident().as_interned_str() { + if name == param.name.ident().name { return Some(param); } } @@ -669,6 +669,12 @@ Some(self.span) } } + + /// The `WhereClause` under normal circumstances points at either the predicates or the empty + /// space where the `where` clause should be. Only of use for diagnostic suggestions. + pub fn span_for_predicates_or_empty_place(&self) -> Span { + self.span + } } /// A single predicate in a where-clause. @@ -766,7 +772,7 @@ /// A list of modules written out in the order in which they /// appear in the crate. This includes the main crate module. - pub modules: BTreeMap<NodeId, ModuleItems>, + pub modules: BTreeMap<HirId, ModuleItems>, } impl Crate { @@ -861,7 +867,7 @@ pub span: Span, /// If true, then there may exist `break 'a` values that aim to /// break out of this block early. - /// Used by `'label: {}` blocks and by `catch` statements. + /// Used by `'label: {}` blocks and by `try {}` blocks. pub targeted_by_break: bool, } @@ -869,7 +875,7 @@ pub struct Pat { #[stable_hasher(ignore)] pub hir_id: HirId, - pub node: PatKind, + pub kind: PatKind, pub span: Span, } @@ -882,44 +888,61 @@ impl Pat { // FIXME(#19596) this is a workaround, but there should be a better way - fn walk_<G>(&self, it: &mut G) -> bool - where G: FnMut(&Pat) -> bool - { + fn walk_short_(&self, it: &mut impl FnMut(&Pat) -> bool) -> bool { if !it(self) { return false; } - match self.node { - PatKind::Binding(.., Some(ref p)) => p.walk_(it), - PatKind::Struct(_, ref fields, _) => { - fields.iter().all(|field| field.pat.walk_(it)) - } - PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => { - s.iter().all(|p| p.walk_(it)) - } - PatKind::Or(ref pats) => pats.iter().all(|p| p.walk_(it)), - PatKind::Box(ref s) | PatKind::Ref(ref s, _) => { - s.walk_(it) - } - PatKind::Slice(ref before, ref slice, ref after) => { + use PatKind::*; + match &self.kind { + Wild | Lit(_) | Range(..) | Binding(.., None) | Path(_) => true, + Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it), + Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), + TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), + Slice(before, slice, after) => { before.iter() .chain(slice.iter()) .chain(after.iter()) - .all(|p| p.walk_(it)) - } - PatKind::Wild | - PatKind::Lit(_) | - PatKind::Range(..) | - PatKind::Binding(..) | - PatKind::Path(_) => { - true + .all(|p| p.walk_short_(it)) } } } - pub fn walk<F>(&self, mut it: F) -> bool - where F: FnMut(&Pat) -> bool - { + /// Walk the pattern in left-to-right order, + /// short circuiting (with `.all(..)`) if `false` is returned. + /// + /// Note that when visiting e.g. `Tuple(ps)`, + /// if visiting `ps[0]` returns `false`, + /// then `ps[1]` will not be visited. + pub fn walk_short(&self, mut it: impl FnMut(&Pat) -> bool) -> bool { + self.walk_short_(&mut it) + } + + // FIXME(#19596) this is a workaround, but there should be a better way + fn walk_(&self, it: &mut impl FnMut(&Pat) -> bool) { + if !it(self) { + return; + } + + use PatKind::*; + match &self.kind { + Wild | Lit(_) | Range(..) | Binding(.., None) | Path(_) => {}, + Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it), + Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), + TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), + Slice(before, slice, after) => { + before.iter() + .chain(slice.iter()) + .chain(after.iter()) + .for_each(|p| p.walk_(it)) + } + } + } + + /// Walk the pattern in left-to-right order. + /// + /// If `it(pat)` returns `false`, the children are not visited. + pub fn walk(&self, mut it: impl FnMut(&Pat) -> bool) { self.walk_(&mut it) } } @@ -972,6 +995,15 @@ Excluded, } +impl fmt::Display for RangeEnd { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + RangeEnd::Included => "..=", + RangeEnd::Excluded => "..", + }) + } +} + #[derive(RustcEncodable, RustcDecodable, Debug, HashStable)] pub enum PatKind { /// Represents a wildcard pattern (i.e., `_`). @@ -1036,9 +1068,16 @@ MutImmutable => MutImmutable, } } + + pub fn invert(self) -> Self { + match self { + MutMutable => MutImmutable, + MutImmutable => MutMutable, + } + } } -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Hash, HashStable)] +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable)] pub enum BinOpKind { /// The `+` operator (addition). Add, @@ -1172,7 +1211,7 @@ pub type BinOp = Spanned<BinOpKind>; -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Hash, HashStable)] +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable)] pub enum UnOp { /// The `*` operator (deferencing). UnDeref, @@ -1204,7 +1243,7 @@ #[derive(RustcEncodable, RustcDecodable)] pub struct Stmt { pub hir_id: HirId, - pub node: StmtKind, + pub kind: StmtKind, pub span: Span, } @@ -1259,15 +1298,15 @@ } /// Represents a single arm of a `match` expression, e.g. -/// `<pats> (if <guard>) => <body>`. +/// `<pat> (if <guard>) => <body>`. #[derive(RustcEncodable, RustcDecodable, Debug, HashStable)] pub struct Arm { #[stable_hasher(ignore)] pub hir_id: HirId, pub span: Span, pub attrs: HirVec<Attribute>, - /// Multiple patterns can be combined with `|` - pub pats: HirVec<P<Pat>>, + /// If this pattern and the optional guard matches, then `body` is evaluated. + pub pat: P<Pat>, /// Optional guard clause. pub guard: Option<Guard>, /// The expression the arm evaluates to if this arm matches. @@ -1342,24 +1381,54 @@ hir_id: self.value.hir_id, } } + + pub fn generator_kind(&self) -> Option<GeneratorKind> { + self.generator_kind + } } /// The type of source expression that caused this generator to be created. -// Not `IsAsync` because we want to eventually add support for `AsyncGen` -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, HashStable, - RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +#[derive(Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable, Debug, Copy)] pub enum GeneratorKind { - /// An `async` block or function. - Async, + /// An explicit `async` block or the body of an async function. + Async(AsyncGeneratorKind), + /// A generator literal created via a `yield` inside a closure. Gen, } impl fmt::Display for GeneratorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + GeneratorKind::Async(k) => fmt::Display::fmt(k, f), + GeneratorKind::Gen => f.write_str("generator"), + } + } +} + +/// In the case of a generator created as part of an async construct, +/// which kind of async construct caused it to be created? +/// +/// This helps error messages but is also used to drive coercions in +/// type-checking (see #60424). +#[derive(Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable, Debug, Copy)] +pub enum AsyncGeneratorKind { + /// An explicit `async` block written by the user. + Block, + + /// An explicit `async` block written by the user. + Closure, + + /// The `async` block generated as the body of an async function. + Fn, +} + +impl fmt::Display for AsyncGeneratorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match self { - GeneratorKind::Async => "`async` object", - GeneratorKind::Gen => "generator", + AsyncGeneratorKind::Block => "`async` block", + AsyncGeneratorKind::Closure => "`async` closure body", + AsyncGeneratorKind::Fn => "`async fn` body", }) } } @@ -1406,7 +1475,7 @@ #[derive(RustcEncodable, RustcDecodable)] pub struct Expr { pub hir_id: HirId, - pub node: ExprKind, + pub kind: ExprKind, pub attrs: ThinVec<Attribute>, pub span: Span, } @@ -1417,7 +1486,7 @@ impl Expr { pub fn precedence(&self) -> ExprPrecedence { - match self.node { + match self.kind { ExprKind::Box(_) => ExprPrecedence::Box, ExprKind::Array(_) => ExprPrecedence::Array, ExprKind::Call(..) => ExprPrecedence::Call, @@ -1450,7 +1519,7 @@ } pub fn is_place_expr(&self) -> bool { - match self.node { + match self.kind { ExprKind::Path(QPath::Resolved(_, ref path)) => { match path.res { Res::Local(..) @@ -1503,6 +1572,19 @@ } } } + + /// If `Self.kind` is `ExprKind::DropTemps(expr)`, drill down until we get a non-`DropTemps` + /// `Expr`. This is used in suggestions to ignore this `ExprKind` as it is semantically + /// silent, only signaling the ownership system. By doing this, suggestions that check the + /// `ExprKind` of any given `Expr` for presentation don't have to care about `DropTemps` + /// beyond remembering to call this function before doing analysis on it. + pub fn peel_drop_temps(&self) -> &Self { + let mut expr = self; + while let ExprKind::DropTemps(inner) = &expr.kind { + expr = inner; + } + expr + } } impl fmt::Debug for Expr { @@ -1535,7 +1617,7 @@ /// Thus, `x.foo::<Bar, Baz>(a, b, c, d)` is represented as /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d])`. MethodCall(P<PathSegment>, Span, HirVec<Expr>), - /// A tuple (e.g., `(a, b, c ,d)`). + /// A tuple (e.g., `(a, b, c, d)`). Tup(HirVec<Expr>), /// A binary operation (e.g., `a + b`, `a * b`). Binary(BinOp, P<Expr>, P<Expr>), @@ -1741,6 +1823,7 @@ pub enum GeneratorMovability { /// May contain self-references, `!Unpin`. Static, + /// Must not contain self-references, `Unpin`. Movable, } @@ -1802,7 +1885,7 @@ pub hir_id: HirId, pub attrs: HirVec<Attribute>, pub generics: Generics, - pub node: TraitItemKind, + pub kind: TraitItemKind, pub span: Span, } @@ -1845,7 +1928,7 @@ pub defaultness: Defaultness, pub attrs: HirVec<Attribute>, pub generics: Generics, - pub node: ImplItemKind, + pub kind: ImplItemKind, pub span: Span, } @@ -1911,7 +1994,7 @@ #[derive(RustcEncodable, RustcDecodable)] pub struct Ty { pub hir_id: HirId, - pub node: TyKind, + pub kind: TyKind, pub span: Span, } @@ -1999,9 +2082,6 @@ Infer, /// Placeholder for a type that has failed to be defined. Err, - /// Placeholder for C-variadic arguments. We "spoof" the `VaListImpl` created - /// from the variadic arguments. This type is only valid up to typeck. - CVarArgs(Lifetime), } #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable)] @@ -2388,7 +2468,7 @@ pub ident: Ident, pub hir_id: HirId, pub attrs: HirVec<Attribute>, - pub node: ItemKind, + pub kind: ItemKind, pub vis: Visibility, pub span: Span, } @@ -2432,7 +2512,7 @@ Fn(P<FnDecl>, FnHeader, Generics, BodyId), /// A module. Mod(Mod), - /// An external module. + /// An external module, e.g. `extern { .. }`. ForeignMod(ForeignMod), /// Module-level inline assembly (from `global_asm!`). GlobalAsm(P<GlobalAsm>), @@ -2553,7 +2633,7 @@ #[stable_hasher(project(name))] pub ident: Ident, pub attrs: HirVec<Attribute>, - pub node: ForeignItemKind, + pub kind: ForeignItemKind, pub hir_id: HirId, pub span: Span, pub vis: Visibility, @@ -2626,6 +2706,11 @@ /// probably isn't set when this is set, this is for foreign items while /// `#[export_name]` is for Rust-defined functions. pub link_name: Option<Symbol>, + /// The `#[link_ordinal = "..."]` attribute, indicating an ordinal an + /// imported function has in the dynamic library. Note that this must not + /// be set when `link_name` is set. This is for foreign items with the + /// "raw-dylib" kind. + pub link_ordinal: Option<usize>, /// The `#[target_feature(enable = "...")]` attribute and the enabled /// features (only enabled features are supported right now). pub target_features: Vec<Symbol>, @@ -2671,9 +2756,11 @@ /// `#[used]`: indicates that LLVM can't eliminate this function (but the /// linker can!). const USED = 1 << 9; - /// #[ffi_returns_twice], indicates that an extern function can return + /// `#[ffi_returns_twice]`, indicates that an extern function can return /// multiple times - const FFI_RETURNS_TWICE = 1 << 10; + const FFI_RETURNS_TWICE = 1 << 10; + /// `#[track_caller]`: allow access to the caller location + const TRACK_CALLER = 1 << 11; } } @@ -2685,6 +2772,7 @@ optimize: OptimizeAttr::None, export_name: None, link_name: None, + link_ordinal: None, target_features: vec![], linkage: None, link_section: None,
diff --git a/src/librustc/hir/pat_util.rs b/src/librustc/hir/pat_util.rs index 0d2c7d3..feb0d97 100644 --- a/src/librustc/hir/pat_util.rs +++ b/src/librustc/hir/pat_util.rs
@@ -45,7 +45,7 @@ impl hir::Pat { pub fn is_refutable(&self) -> bool { - match self.node { + match self.kind { PatKind::Lit(_) | PatKind::Range(..) | PatKind::Path(hir::QPath::Resolved(Some(..), _)) | @@ -66,50 +66,70 @@ /// Call `f` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }` - pub fn each_binding<F>(&self, mut f: F) - where F: FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident), - { + pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident)) { self.walk(|p| { - if let PatKind::Binding(binding_mode, _, ident, _) = p.node { + if let PatKind::Binding(binding_mode, _, ident, _) = p.kind { f(binding_mode, p.hir_id, p.span, ident); } true }); } + /// Call `f` on every "binding" in a pattern, e.g., on `a` in + /// `match foo() { Some(a) => (), None => () }`. + /// + /// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited. + pub fn each_binding_or_first( + &self, + f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident), + ) { + self.walk(|p| match &p.kind { + PatKind::Or(ps) => { + ps[0].each_binding_or_first(f); + false + }, + PatKind::Binding(bm, _, ident, _) => { + f(*bm, p.hir_id, p.span, *ident); + true + } + _ => true, + }) + } + /// Checks if the pattern contains any patterns that bind something to /// an ident, e.g., `foo`, or `Foo(foo)` or `foo @ Bar(..)`. pub fn contains_bindings(&self) -> bool { - let mut contains_bindings = false; - self.walk(|p| { - if let PatKind::Binding(..) = p.node { - contains_bindings = true; - false // there's at least one binding, can short circuit now. - } else { - true - } - }); - contains_bindings + self.satisfies(|p| match p.kind { + PatKind::Binding(..) => true, + _ => false, + }) } /// Checks if the pattern contains any patterns that bind something to /// an ident or wildcard, e.g., `foo`, or `Foo(_)`, `foo @ Bar(..)`, pub fn contains_bindings_or_wild(&self) -> bool { - let mut contains_bindings = false; - self.walk(|p| { - match p.node { - PatKind::Binding(..) | PatKind::Wild => { - contains_bindings = true; - false // there's at least one binding/wildcard, can short circuit now. - } - _ => true + self.satisfies(|p| match p.kind { + PatKind::Binding(..) | PatKind::Wild => true, + _ => false, + }) + } + + /// Checks if the pattern satisfies the given predicate on some sub-pattern. + fn satisfies(&self, pred: impl Fn(&Self) -> bool) -> bool { + let mut satisfies = false; + self.walk_short(|p| { + if pred(p) { + satisfies = true; + false // Found one, can short circuit now. + } else { + true } }); - contains_bindings + satisfies } pub fn simple_ident(&self) -> Option<ast::Ident> { - match self.node { + match self.kind { PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, None) | PatKind::Binding(hir::BindingAnnotation::Mutable, _, ident, None) => Some(ident), _ => None, @@ -119,20 +139,20 @@ /// Returns variants that are necessary to exist for the pattern to match. pub fn necessary_variants(&self) -> Vec<DefId> { let mut variants = vec![]; - self.walk(|p| { - match p.node { - PatKind::Path(hir::QPath::Resolved(_, ref path)) | - PatKind::TupleStruct(hir::QPath::Resolved(_, ref path), ..) | - PatKind::Struct(hir::QPath::Resolved(_, ref path), ..) => { - match path.res { - Res::Def(DefKind::Variant, id) => variants.push(id), - Res::Def(DefKind::Ctor(CtorOf::Variant, ..), id) => variants.push(id), - _ => () - } + self.walk(|p| match &p.kind { + PatKind::Or(_) => false, + PatKind::Path(hir::QPath::Resolved(_, path)) | + PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..) | + PatKind::Struct(hir::QPath::Resolved(_, path), ..) => { + if let Res::Def(DefKind::Variant, id) + | Res::Def(DefKind::Ctor(CtorOf::Variant, ..), id) + = path.res + { + variants.push(id); } - _ => () + true } - true + _ => true, }); variants.sort(); variants.dedup(); @@ -148,33 +168,14 @@ let mut result = None; self.each_binding(|annotation, _, _, _| { match annotation { - hir::BindingAnnotation::Ref => { - match result { - None | Some(hir::MutImmutable) => result = Some(hir::MutImmutable), - _ => (), - } + hir::BindingAnnotation::Ref => match result { + None | Some(hir::MutImmutable) => result = Some(hir::MutImmutable), + _ => {} } hir::BindingAnnotation::RefMut => result = Some(hir::MutMutable), - _ => (), + _ => {} } }); result } } - -impl hir::Arm { - /// Checks if the patterns for this arm contain any `ref` or `ref mut` - /// bindings, and if yes whether its containing mutable ones or just immutables ones. - pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> { - // FIXME(tschottdorf): contains_explicit_ref_binding() must be removed - // for #42640 (default match binding modes). - // - // See #44848. - self.pats.iter() - .filter_map(|pat| pat.contains_explicit_ref_binding()) - .max_by_key(|m| match *m { - hir::MutMutable => 1, - hir::MutImmutable => 0, - }) - } -}
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index cfbfb5e..64b355f 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs
@@ -1,10 +1,10 @@ use rustc_target::spec::abi::Abi; use syntax::ast; use syntax::source_map::{SourceMap, Spanned}; -use syntax::parse::ParseSess; use syntax::print::pp::{self, Breaks}; use syntax::print::pp::Breaks::{Consistent, Inconsistent}; use syntax::print::pprust::{self, Comments, PrintState}; +use syntax::sess::ParseSess; use syntax::symbol::kw; use syntax::util::parser::{self, AssocOp, Fixity}; use syntax_pos::{self, BytePos, FileName}; @@ -286,7 +286,7 @@ pub fn print_type(&mut self, ty: &hir::Ty) { self.maybe_print_comment(ty.span.lo()); self.ibox(0); - match ty.node { + match ty.kind { hir::TyKind::Slice(ref ty) => { self.s.word("["); self.print_type(&ty); @@ -361,9 +361,6 @@ self.s.word("/*ERROR*/"); self.pclose(); } - hir::TyKind::CVarArgs(_) => { - self.s.word("..."); - } } self.end() } @@ -372,7 +369,7 @@ self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); self.print_outer_attributes(&item.attrs); - match item.node { + match item.kind { hir::ForeignItemKind::Fn(ref decl, ref arg_names, ref generics) => { self.head(""); self.print_fn(decl, @@ -474,7 +471,7 @@ self.maybe_print_comment(item.span.lo()); self.print_outer_attributes(&item.attrs); self.ann.pre(self, AnnNode::Item(item)); - match item.node { + match item.kind { hir::ItemKind::ExternCrate(orig_name) => { self.head(visibility_qualified(&item.vis, "extern crate")); if let Some(orig_name) = orig_name { @@ -858,7 +855,7 @@ self.hardbreak_if_not_bol(); self.maybe_print_comment(ti.span.lo()); self.print_outer_attributes(&ti.attrs); - match ti.node { + match ti.kind { hir::TraitItemKind::Const(ref ty, default) => { let vis = Spanned { span: syntax_pos::DUMMY_SP, node: hir::VisibilityKind::Inherited }; @@ -896,7 +893,7 @@ self.print_outer_attributes(&ii.attrs); self.print_defaultness(ii.defaultness); - match ii.node { + match ii.kind { hir::ImplItemKind::Const(ref ty, expr) => { self.print_associated_const(ii.ident, &ty, Some(expr), &ii.vis); } @@ -944,7 +941,7 @@ pub fn print_stmt(&mut self, st: &hir::Stmt) { self.maybe_print_comment(st.span.lo()); - match st.node { + match st.kind { hir::StmtKind::Local(ref loc) => { self.print_local(loc.init.as_deref(), |this| this.print_local_decl(&loc)); } @@ -961,7 +958,7 @@ self.s.word(";"); } } - if stmt_ends_with_semi(&st.node) { + if stmt_ends_with_semi(&st.kind) { self.s.word(";"); } self.maybe_print_trailing_comment(st.span, None) @@ -1035,7 +1032,7 @@ /// Print an expr using syntax that's acceptable in a condition position, such as the `cond` in /// `if cond { ... }`. pub fn print_expr_as_cond(&mut self, expr: &hir::Expr) { - let needs_par = match expr.node { + let needs_par = match expr.kind { // These cases need parens due to the parse error observed in #26461: `if return {}` // parses as the erroneous construct `if (return {})`, not `if (return) {}`. hir::ExprKind::Closure(..) | @@ -1119,11 +1116,10 @@ } fn print_expr_call(&mut self, func: &hir::Expr, args: &[hir::Expr]) { - let prec = - match func.node { - hir::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, - _ => parser::PREC_POSTFIX, - }; + let prec = match func.kind { + hir::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, + _ => parser::PREC_POSTFIX, + }; self.print_expr_maybe_paren(func, prec); self.print_call_post(args) @@ -1161,7 +1157,7 @@ Fixity::None => (prec + 1, prec + 1), }; - let left_prec = match (&lhs.node, op.node) { + let left_prec = match (&lhs.kind, op.node) { // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead // of `(x as i32) < ...`. We need to convince it _not_ to do that. @@ -1200,7 +1196,7 @@ self.print_outer_attributes(&expr.attrs); self.ibox(INDENT_UNIT); self.ann.pre(self, AnnNode::Expr(expr)); - match expr.node { + match expr.kind { hir::ExprKind::Box(ref expr) => { self.word_space("box"); self.print_expr_maybe_paren(expr, parser::PREC_PREFIX); @@ -1527,9 +1523,17 @@ colons_before_params) } hir::QPath::TypeRelative(ref qself, ref item_segment) => { - self.s.word("<"); - self.print_type(qself); - self.s.word(">"); + // If we've got a compound-qualified-path, let's push an additional pair of angle + // brackets, so that we pretty-print `<<A::B>::C>` as `<A::B>::C`, instead of just + // `A::B::C` (since the latter could be ambiguous to the user) + if let hir::TyKind::Path(hir::QPath::Resolved(None, _)) = &qself.kind { + self.print_type(qself); + } else { + self.s.word("<"); + self.print_type(qself); + self.s.word(">"); + } + self.s.word("::"); self.print_ident(item_segment.ident); self.print_generic_args(item_segment.generic_args(), @@ -1618,7 +1622,7 @@ self.ann.pre(self, AnnNode::Pat(pat)); // Pat isn't normalized, but the beauty of it // is that it doesn't matter - match pat.node { + match pat.kind { PatKind::Wild => self.s.word("_"), PatKind::Binding(binding_mode, _, ident, ref sub) => { match binding_mode { @@ -1711,7 +1715,7 @@ self.pclose(); } PatKind::Box(ref inner) => { - let is_range_inner = match inner.node { + let is_range_inner = match inner.kind { PatKind::Range(..) => true, _ => false, }; @@ -1725,7 +1729,7 @@ } } PatKind::Ref(ref inner, mutbl) => { - let is_range_inner = match inner.node { + let is_range_inner = match inner.kind { PatKind::Range(..) => true, _ => false, }; @@ -1758,7 +1762,7 @@ if !before.is_empty() { self.word_space(","); } - if let PatKind::Wild = p.node { + if let PatKind::Wild = p.kind { // Print nothing. } else { self.print_pat(&p); @@ -1790,16 +1794,7 @@ self.ann.pre(self, AnnNode::Arm(arm)); self.ibox(0); self.print_outer_attributes(&arm.attrs); - let mut first = true; - for p in &arm.pats { - if first { - first = false; - } else { - self.s.space(); - self.word_space("|"); - } - self.print_pat(&p); - } + self.print_pat(&arm.pat); self.s.space(); if let Some(ref g) = arm.guard { match g { @@ -1812,7 +1807,7 @@ } self.word_space("=>"); - match arm.body.node { + match arm.body.kind { hir::ExprKind::Block(ref blk, opt_label) => { if let Some(label) = opt_label { self.print_ident(label.ident); @@ -1890,7 +1885,7 @@ s.ann.nested(s, Nested::BodyParamPat(body_id, i)); i += 1; - if let hir::TyKind::Infer = ty.node { + if let hir::TyKind::Infer = ty.kind { // Print nothing. } else { s.s.word(":"); @@ -2231,7 +2226,7 @@ // // Duplicated from `parse::classify`, but adapted for the HIR. fn expr_requires_semi_to_be_stmt(e: &hir::Expr) -> bool { - match e.node { + match e.kind { hir::ExprKind::Match(..) | hir::ExprKind::Block(..) | hir::ExprKind::Loop(..) => false, @@ -2282,7 +2277,7 @@ /// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and /// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not. fn contains_exterior_struct_lit(value: &hir::Expr) -> bool { - match value.node { + match value.kind { hir::ExprKind::Struct(..) => true, hir::ExprKind::Assign(ref lhs, ref rhs) |
diff --git a/src/librustc/hir/ptr.rs b/src/librustc/hir/ptr.rs index 1976b4c..7ee461a 100644 --- a/src/librustc/hir/ptr.rs +++ b/src/librustc/hir/ptr.rs
@@ -9,10 +9,9 @@ use rustc_serialize::{Encodable, Decodable, Encoder, Decoder}; -use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, - HashStable}; +use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; /// An owned smart pointer. -#[derive(Hash, PartialEq, Eq)] +#[derive(PartialEq, Eq)] pub struct P<T: ?Sized> { ptr: Box<T> } @@ -133,9 +132,7 @@ impl<CTX, T> HashStable<CTX> for P<T> where T: ?Sized + HashStable<CTX> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { (**self).hash_stable(hcx, hasher); } }
diff --git a/src/librustc/hir/upvars.rs b/src/librustc/hir/upvars.rs index cc532cb..5c5f7f6 100644 --- a/src/librustc/hir/upvars.rs +++ b/src/librustc/hir/upvars.rs
@@ -47,7 +47,7 @@ } fn visit_pat(&mut self, pat: &'tcx hir::Pat) { - if let hir::PatKind::Binding(_, hir_id, ..) = pat.node { + if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind { self.locals.insert(hir_id); } intravisit::walk_pat(self, pat); @@ -82,7 +82,7 @@ } fn visit_expr(&mut self, expr: &'tcx hir::Expr) { - if let hir::ExprKind::Closure(..) = expr.node { + if let hir::ExprKind::Closure(..) = expr.kind { let closure_def_id = self.tcx.hir().local_def_id(expr.hir_id); if let Some(upvars) = self.tcx.upvars(closure_def_id) { // Every capture of a closure expression is a local in scope,
diff --git a/src/librustc/ich/hcx.rs b/src/librustc/ich/hcx.rs index 182a9ad..957dab3 100644 --- a/src/librustc/ich/hcx.rs +++ b/src/librustc/ich/hcx.rs
@@ -13,14 +13,13 @@ use syntax::ast; use syntax::source_map::SourceMap; -use syntax::ext::hygiene::SyntaxContext; use syntax::symbol::Symbol; use syntax::tokenstream::DelimSpan; use syntax_pos::{Span, DUMMY_SP}; -use syntax_pos::hygiene; +use syntax_pos::hygiene::{self, SyntaxContext}; use rustc_data_structures::stable_hasher::{ - HashStable, StableHasher, StableHasherResult, ToStableHashKey, + HashStable, StableHasher, ToStableHashKey, }; use rustc_data_structures::fx::{FxHashSet, FxHashMap}; use smallvec::SmallVec; @@ -219,9 +218,7 @@ impl<'a> crate::dep_graph::DepGraphSafe for StableHashingContext<'a> {} impl<'a> HashStable<StableHashingContext<'a>> for hir::BodyId { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { if hcx.hash_bodies() { hcx.body_resolver.body(*self).hash_stable(hcx, hasher); } @@ -230,9 +227,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for hir::HirId { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { match hcx.node_id_hashing_mode { NodeIdHashingMode::Ignore => { // Don't do anything. @@ -263,9 +258,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for ast::NodeId { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { match hcx.node_id_hashing_mode { NodeIdHashingMode::Ignore => { // Don't do anything. @@ -298,9 +291,7 @@ /// codepoint offsets. For the purpose of the hash that's sufficient. /// Also, hashing filenames is expensive so we avoid doing it twice when the /// span starts and ends in the same file, which is almost always the case. - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { const TAG_VALID_SPAN: u8 = 0; const TAG_INVALID_SPAN: u8 = 1; const TAG_EXPANSION: u8 = 0; @@ -379,24 +370,18 @@ } impl<'a> HashStable<StableHashingContext<'a>> for DelimSpan { - fn hash_stable<W: StableHasherResult>( - &self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>, - ) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.open.hash_stable(hcx, hasher); self.close.hash_stable(hcx, hasher); } } -pub fn hash_stable_trait_impls<'a, W>( +pub fn hash_stable_trait_impls<'a>( hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>, + hasher: &mut StableHasher, blanket_impls: &[DefId], non_blanket_impls: &FxHashMap<fast_reject::SimplifiedType, Vec<DefId>>, -) where - W: StableHasherResult, -{ +) { { let mut blanket_impls: SmallVec<[_; 8]> = blanket_impls .iter()
diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index 6e6492d..c0255e5 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs
@@ -6,9 +6,7 @@ use crate::hir::def_id::{DefId, LocalDefId, CrateNum, CRATE_DEF_INDEX}; use crate::ich::{StableHashingContext, NodeIdHashingMode, Fingerprint}; -use rustc_data_structures::stable_hasher::{ - HashStable, ToStableHashKey, StableHasher, StableHasherResult, -}; +use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, StableHasher}; use smallvec::SmallVec; use std::mem; use syntax::ast; @@ -16,9 +14,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for DefId { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(*self).hash_stable(hcx, hasher); } } @@ -34,9 +30,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for LocalDefId { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(self.to_def_id()).hash_stable(hcx, hasher); } } @@ -52,9 +46,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for CrateNum { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.def_path_hash(DefId { krate: *self, index: CRATE_DEF_INDEX @@ -92,9 +84,7 @@ // in "DefPath Mode". impl<'a> HashStable<StableHashingContext<'a>> for hir::ItemId { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ItemId { id } = *self; @@ -106,9 +96,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for hir::TraitItemId { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::TraitItemId { hir_id } = * self; @@ -120,9 +108,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for hir::ImplItemId { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ImplItemId { hir_id } = * self; @@ -138,17 +124,15 @@ }); impl<'a> HashStable<StableHashingContext<'a>> for hir::Ty { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.while_hashing_hir_bodies(true, |hcx| { let hir::Ty { hir_id: _, - ref node, + ref kind, ref span, } = *self; - node.hash_stable(hcx, hasher); + kind.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); }) } @@ -158,7 +142,7 @@ impl_stable_hash_for!(struct hir::Stmt { hir_id, - node, + kind, span, }); @@ -166,19 +150,17 @@ impl_stable_hash_for_spanned!(ast::Name); impl<'a> HashStable<StableHashingContext<'a>> for hir::Expr { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.while_hashing_hir_bodies(true, |hcx| { let hir::Expr { hir_id: _, ref span, - ref node, + ref kind, ref attrs } = *self; span.hash_stable(hcx, hasher); - node.hash_stable(hcx, hasher); + kind.hash_stable(hcx, hasher); attrs.hash_stable(hcx, hasher); }) } @@ -192,15 +174,13 @@ }); impl<'a> HashStable<StableHashingContext<'a>> for hir::TraitItem { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::TraitItem { hir_id: _, ident, ref attrs, ref generics, - ref node, + ref kind, span } = *self; @@ -208,7 +188,7 @@ ident.name.hash_stable(hcx, hasher); attrs.hash_stable(hcx, hasher); generics.hash_stable(hcx, hasher); - node.hash_stable(hcx, hasher); + kind.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); }); } @@ -216,9 +196,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for hir::ImplItem { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::ImplItem { hir_id: _, ident, @@ -226,7 +204,7 @@ defaultness, ref attrs, ref generics, - ref node, + ref kind, span } = *self; @@ -236,7 +214,7 @@ defaultness.hash_stable(hcx, hasher); attrs.hash_stable(hcx, hasher); generics.hash_stable(hcx, hasher); - node.hash_stable(hcx, hasher); + kind.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); }); } @@ -248,9 +226,7 @@ }); impl<'a> HashStable<StableHashingContext<'a>> for hir::VisibilityKind { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { hir::VisibilityKind::Public | @@ -273,9 +249,7 @@ impl_stable_hash_for_spanned!(hir::VisibilityKind); impl<'a> HashStable<StableHashingContext<'a>> for hir::Mod { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Mod { inner: ref inner_span, ref item_ids, @@ -305,14 +279,12 @@ impl<'a> HashStable<StableHashingContext<'a>> for hir::Item { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Item { ident, ref attrs, hir_id: _, - ref node, + ref kind, ref vis, span } = *self; @@ -320,7 +292,7 @@ hcx.hash_hir_item_like(|hcx| { ident.name.hash_stable(hcx, hasher); attrs.hash_stable(hcx, hasher); - node.hash_stable(hcx, hasher); + kind.hash_stable(hcx, hasher); vis.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); }); @@ -328,9 +300,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for hir::Body { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let hir::Body { params, value, @@ -359,9 +329,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for hir::def_id::DefIndex { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.local_def_path_hash(*self).hash_stable(hcx, hasher); } } @@ -376,17 +344,13 @@ } impl<'a> HashStable<StableHashingContext<'a>> for crate::middle::lang_items::LangItem { - fn hash_stable<W: StableHasherResult>(&self, - _: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, _: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { ::std::hash::Hash::hash(self, hasher); } } impl<'a> HashStable<StableHashingContext<'a>> for hir::TraitCandidate { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { let hir::TraitCandidate { def_id, @@ -418,17 +382,13 @@ } impl<'hir> HashStable<StableHashingContext<'hir>> for attr::InlineAttr { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'hir>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'hir>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); } } impl<'hir> HashStable<StableHashingContext<'hir>> for attr::OptimizeAttr { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'hir>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'hir>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); } }
diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index a33181e..4fd4e25 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs
@@ -9,53 +9,49 @@ use syntax::ast; use syntax::feature_gate; use syntax::parse::token; -use syntax::symbol::InternedString; +use syntax::symbol::LocalInternedString; use syntax::tokenstream; use syntax_pos::SourceFile; use crate::hir::def_id::{DefId, CrateNum, CRATE_DEF_INDEX}; use smallvec::SmallVec; -use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, - StableHasher, StableHasherResult}; +use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, StableHasher}; -impl<'a> HashStable<StableHashingContext<'a>> for InternedString { +impl<'a> HashStable<StableHashingContext<'a>> for LocalInternedString { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { - self.with(|s| s.hash_stable(hcx, hasher)) + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let str = self as &str; + str.hash_stable(hcx, hasher) } } -impl<'a> ToStableHashKey<StableHashingContext<'a>> for InternedString { - type KeyType = InternedString; +impl<'a> ToStableHashKey<StableHashingContext<'a>> for LocalInternedString { + type KeyType = LocalInternedString; #[inline] fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) - -> InternedString { + -> LocalInternedString { self.clone() } } impl<'a> HashStable<StableHashingContext<'a>> for ast::Name { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.as_str().hash_stable(hcx, hasher); } } impl<'a> ToStableHashKey<StableHashingContext<'a>> for ast::Name { - type KeyType = InternedString; + type KeyType = LocalInternedString; #[inline] fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) - -> InternedString { - self.as_interned_str() + -> LocalInternedString { + self.as_str() } } @@ -64,7 +60,7 @@ Intel }); -impl_stable_hash_for!(enum ::syntax::ext::base::MacroKind { +impl_stable_hash_for!(enum ::syntax_pos::hygiene::MacroKind { Bang, Attr, Derive, @@ -84,6 +80,7 @@ Msp430Interrupt, X86Interrupt, AmdGpuKernel, + EfiApi, Rust, C, System, @@ -110,9 +107,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ::syntax::attr::StabilityLevel { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { ::syntax::attr::StabilityLevel::Unstable { ref reason, ref issue, ref is_soft } => { @@ -142,7 +137,7 @@ }); impl_stable_hash_for!(struct ::syntax::ast::Lit { - node, + kind, token, span }); @@ -172,9 +167,7 @@ impl_stable_hash_for!(enum ::syntax::ast::AttrStyle { Outer, Inner }); impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { if self.len() == 0 { self.len().hash_stable(hcx, hasher); return @@ -197,9 +190,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for ast::Path { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.segments.len().hash_stable(hcx, hasher); for segment in &self.segments { segment.ident.name.hash_stable(hcx, hasher); @@ -207,37 +198,34 @@ } } +impl_stable_hash_for!(struct ::syntax::ast::AttrItem { + path, + tokens, +}); + impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { // Make sure that these have been filtered out. debug_assert!(!self.ident().map_or(false, |ident| hcx.is_ignored_attr(ident.name))); debug_assert!(!self.is_sugared_doc); let ast::Attribute { + ref item, id: _, style, - ref path, - ref tokens, is_sugared_doc: _, span, } = *self; + item.hash_stable(hcx, hasher); style.hash_stable(hcx, hasher); - path.hash_stable(hcx, hasher); - for tt in tokens.trees() { - tt.hash_stable(hcx, hasher); - } span.hash_stable(hcx, hasher); } } impl<'a> HashStable<StableHashingContext<'a>> for tokenstream::TokenTree { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { tokenstream::TokenTree::Token(ref token) => { @@ -256,9 +244,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for tokenstream::TokenStream { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { for sub_tt in self.trees() { sub_tt.hash_stable(hcx, hasher); } @@ -285,9 +271,7 @@ }); impl<'a> HashStable<StableHashingContext<'a>> for token::TokenKind { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { token::Eq | @@ -361,7 +345,7 @@ impl_stable_hash_for!(struct ::syntax::ast::MetaItem { path, - node, + kind, span }); @@ -426,9 +410,7 @@ }); impl<'a> HashStable<StableHashingContext<'a>> for SourceFile { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let SourceFile { name: _, // We hash the smaller name_hash instead of this name_hash, @@ -444,6 +426,7 @@ ref lines, ref multibyte_chars, ref non_narrow_chars, + ref normalized_pos, } = *self; (name_hash as u64).hash_stable(hcx, hasher); @@ -472,6 +455,12 @@ for &char_pos in non_narrow_chars.iter() { stable_non_narrow_char(char_pos, start_pos).hash_stable(hcx, hasher); } + + normalized_pos.len().hash_stable(hcx, hasher); + for &char_pos in normalized_pos.iter() { + stable_normalized_pos(char_pos, start_pos).hash_stable(hcx, hasher); + } + } } @@ -501,12 +490,20 @@ (pos.0 - source_file_start.0, width as u32) } +fn stable_normalized_pos(np: ::syntax_pos::NormalizedPos, + source_file_start: ::syntax_pos::BytePos) + -> (u32, u32) { + let ::syntax_pos::NormalizedPos { + pos, + diff + } = np; + + (pos.0 - source_file_start.0, diff) +} + + impl<'tcx> HashStable<StableHashingContext<'tcx>> for feature_gate::Features { - fn hash_stable<W: StableHasherResult>( - &self, - hcx: &mut StableHashingContext<'tcx>, - hasher: &mut StableHasher<W>, - ) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { // Unfortunately we cannot exhaustively list fields here, since the // struct is macro generated. self.declared_lang_features.hash_stable(hcx, hasher);
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index f230c53..c643baf 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs
@@ -3,8 +3,7 @@ use crate::ich::{Fingerprint, StableHashingContext, NodeIdHashingMode}; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, - StableHasher, StableHasherResult}; +use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, StableHasher}; use std::cell::RefCell; use std::mem; use crate::middle::region; @@ -15,9 +14,7 @@ where T: HashStable<StableHashingContext<'a>>, { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { thread_local! { static CACHE: RefCell<FxHashMap<(usize, usize), Fingerprint>> = RefCell::new(Default::default()); @@ -56,19 +53,15 @@ } } -impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::Kind<'tcx> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { +impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::GenericArg<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.unpack().hash_stable(hcx, hasher); } } impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { ty::ReErased | @@ -112,31 +105,21 @@ impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionVid { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.index().hash_stable(hcx, hasher); } } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::ConstVid<'tcx> { #[inline] - fn hash_stable<W: StableHasherResult>( - &self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>, - ) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.index.hash_stable(hcx, hasher); } } impl<'tcx> HashStable<StableHashingContext<'tcx>> for ty::BoundVar { #[inline] - fn hash_stable<W: StableHasherResult>( - &self, - hcx: &mut StableHashingContext<'tcx>, - hasher: &mut StableHasher<W>, - ) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { self.index().hash_stable(hcx, hasher); } } @@ -145,20 +128,14 @@ where T: HashStable<StableHashingContext<'a>>, { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.skip_binder().hash_stable(hcx, hasher); } } // AllocIds get resolved to whatever they point to (to be stable) impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId { - fn hash_stable<W: StableHasherResult>( - &self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>, - ) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { ty::tls::with_opt(|tcx| { trace!("hashing {:?}", *self); let tcx = tcx.expect("can't hash AllocIds during hir lowering"); @@ -174,11 +151,7 @@ where Tag: HashStable<StableHashingContext<'a>>, { - fn hash_stable<W: StableHasherResult>( - &self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>, - ) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.len().hash_stable(hcx, hasher); for reloc in self.iter() { reloc.hash_stable(hcx, hasher); @@ -201,9 +174,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for ty::TyVid { - fn hash_stable<W: StableHasherResult>(&self, - _hcx: &mut StableHashingContext<'a>, - _hasher: &mut StableHasher<W>) { + fn hash_stable(&self, _hcx: &mut StableHashingContext<'a>, _hasher: &mut StableHasher) { // `TyVid` values are confined to an inference context and hence // should not be hashed. bug!("ty::TyKind::hash_stable() - can't hash a TyVid {:?}.", *self) @@ -211,9 +182,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for ty::IntVid { - fn hash_stable<W: StableHasherResult>(&self, - _hcx: &mut StableHashingContext<'a>, - _hasher: &mut StableHasher<W>) { + fn hash_stable(&self, _hcx: &mut StableHashingContext<'a>, _hasher: &mut StableHasher) { // `IntVid` values are confined to an inference context and hence // should not be hashed. bug!("ty::TyKind::hash_stable() - can't hash an IntVid {:?}.", *self) @@ -221,9 +190,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for ty::FloatVid { - fn hash_stable<W: StableHasherResult>(&self, - _hcx: &mut StableHashingContext<'a>, - _hasher: &mut StableHasher<W>) { + fn hash_stable(&self, _hcx: &mut StableHashingContext<'a>, _hasher: &mut StableHasher) { // `FloatVid` values are confined to an inference context and hence // should not be hashed. bug!("ty::TyKind::hash_stable() - can't hash a FloatVid {:?}.", *self) @@ -234,18 +201,14 @@ where T: HashStable<StableHashingContext<'a>>, { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.borrow().hash_stable(hcx, hasher); } } impl<'a> HashStable<StableHashingContext<'a>> for crate::middle::privacy::AccessLevels { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { let crate::middle::privacy::AccessLevels { ref map
diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index db72487..49a2c90 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs
@@ -13,12 +13,12 @@ use crate::mir::interpret::ConstValue; use std::sync::atomic::Ordering; use crate::ty::fold::{TypeFoldable, TypeFolder}; -use crate::ty::subst::Kind; +use crate::ty::subst::GenericArg; use crate::ty::{self, BoundVar, InferConst, List, Ty, TyCtxt, TypeFlags}; use crate::ty::flags::FlagComputation; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::indexed_vec::Idx; +use rustc_index::vec::Idx; use smallvec::SmallVec; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { @@ -282,7 +282,7 @@ query_state: &'cx mut OriginalQueryValues<'tcx>, // Note that indices is only used once `var_values` is big enough to be // heap-allocated. - indices: FxHashMap<Kind<'tcx>, BoundVar>, + indices: FxHashMap<GenericArg<'tcx>, BoundVar>, canonicalize_region_mode: &'cx dyn CanonicalizeRegionMode, needs_canonical_flags: TypeFlags, @@ -343,7 +343,7 @@ } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.sty { + match t.kind { ty::Infer(ty::TyVar(vid)) => { debug!("canonical: type var found with vid {:?}", vid); match self.infcx.unwrap().probe_ty_var(vid) { @@ -468,7 +468,7 @@ ConstValue::Infer(InferConst::Fresh(_)) => { bug!("encountered a fresh const during canonicalization") } - ConstValue::Infer(InferConst::Canonical(debruijn, _)) => { + ConstValue::Bound(debruijn, _) => { if debruijn >= self.binder_index { bug!("escaping bound type during canonicalization") } else { @@ -566,7 +566,7 @@ /// or returns an existing variable if `kind` has already been /// seen. `kind` is expected to be an unbound variable (or /// potentially a free region). - fn canonical_var(&mut self, info: CanonicalVarInfo, kind: Kind<'tcx>) -> BoundVar { + fn canonical_var(&mut self, info: CanonicalVarInfo, kind: GenericArg<'tcx>) -> BoundVar { let Canonicalizer { variables, query_state, @@ -700,8 +700,8 @@ let var = self.canonical_var(info, const_var.into()); self.tcx().mk_const( ty::Const { - val: ConstValue::Infer(InferConst::Canonical(self.binder_index, var.into())), - ty: const_var.ty, + val: ConstValue::Bound(self.binder_index, var.into()), + ty: self.fold_ty(const_var.ty), } ) }
diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 6840611..d833fee 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs
@@ -25,15 +25,15 @@ use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind}; use crate::infer::region_constraints::MemberConstraint; use crate::mir::interpret::ConstValue; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc_index::vec::IndexVec; use rustc_macros::HashStable; use rustc_serialize::UseSpecializedDecodable; use smallvec::SmallVec; use std::ops::Index; use syntax::source_map::Span; use crate::ty::fold::TypeFoldable; -use crate::ty::subst::Kind; -use crate::ty::{self, BoundVar, InferConst, Lift, List, Region, TyCtxt}; +use crate::ty::subst::GenericArg; +use crate::ty::{self, BoundVar, Lift, List, Region, TyCtxt}; mod canonicalizer; @@ -66,14 +66,14 @@ /// canonicalized query response. #[derive(Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable, HashStable)] pub struct CanonicalVarValues<'tcx> { - pub var_values: IndexVec<BoundVar, Kind<'tcx>>, + pub var_values: IndexVec<BoundVar, GenericArg<'tcx>>, } /// When we canonicalize a value to form a query, we wind up replacing /// various parts of it with canonical variables. This struct stores /// those replaced bits to remember for when we process the query /// result. -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] +#[derive(Clone, Debug)] pub struct OriginalQueryValues<'tcx> { /// Map from the universes that appear in the query to the /// universes in the caller context. For the time being, we only @@ -83,7 +83,7 @@ /// This is equivalent to `CanonicalVarValues`, but using a /// `SmallVec` yields a significant performance win. - pub var_values: SmallVec<[Kind<'tcx>; 8]>, + pub var_values: SmallVec<[GenericArg<'tcx>; 8]>, } impl Default for OriginalQueryValues<'tcx> { @@ -308,7 +308,7 @@ } pub type QueryOutlivesConstraint<'tcx> = - ty::Binder<ty::OutlivesPredicate<Kind<'tcx>, Region<'tcx>>>; + ty::Binder<ty::OutlivesPredicate<GenericArg<'tcx>, Region<'tcx>>>; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// Creates a substitution S for the canonical value with fresh @@ -359,7 +359,7 @@ variables: &List<CanonicalVarInfo>, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> CanonicalVarValues<'tcx> { - let var_values: IndexVec<BoundVar, Kind<'tcx>> = variables + let var_values: IndexVec<BoundVar, GenericArg<'tcx>> = variables .iter() .map(|info| self.instantiate_canonical_var(span, *info, &universe_map)) .collect(); @@ -376,7 +376,7 @@ span: Span, cv_info: CanonicalVarInfo, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, - ) -> Kind<'tcx> { + ) -> GenericArg<'tcx> { match cv_info.kind { CanonicalVarKind::Ty(ty_kind) => { let ty = match ty_kind { @@ -495,24 +495,22 @@ /// we'll return a substitution `subst` with: /// `subst.var_values == [Type(^0), Lifetime(^1), Type(^2)]`. pub fn make_identity(&self, tcx: TyCtxt<'tcx>) -> Self { - use crate::ty::subst::UnpackedKind; + use crate::ty::subst::GenericArgKind; CanonicalVarValues { var_values: self.var_values.iter() .zip(0..) .map(|(kind, i)| match kind.unpack() { - UnpackedKind::Type(..) => tcx.mk_ty( + GenericArgKind::Type(..) => tcx.mk_ty( ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i).into()) ).into(), - UnpackedKind::Lifetime(..) => tcx.mk_region( + GenericArgKind::Lifetime(..) => tcx.mk_region( ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(i)) ).into(), - UnpackedKind::Const(ct) => { + GenericArgKind::Const(ct) => { tcx.mk_const(ty::Const { ty: ct.ty, - val: ConstValue::Infer( - InferConst::Canonical(ty::INNERMOST, ty::BoundVar::from_u32(i)) - ), + val: ConstValue::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i)), }).into() } }) @@ -522,8 +520,8 @@ } impl<'a, 'tcx> IntoIterator for &'a CanonicalVarValues<'tcx> { - type Item = Kind<'tcx>; - type IntoIter = ::std::iter::Cloned<::std::slice::Iter<'a, Kind<'tcx>>>; + type Item = GenericArg<'tcx>; + type IntoIter = ::std::iter::Cloned<::std::slice::Iter<'a, GenericArg<'tcx>>>; fn into_iter(self) -> Self::IntoIter { self.var_values.iter().cloned() @@ -570,9 +568,9 @@ } impl<'tcx> Index<BoundVar> for CanonicalVarValues<'tcx> { - type Output = Kind<'tcx>; + type Output = GenericArg<'tcx>; - fn index(&self, value: BoundVar) -> &Kind<'tcx> { + fn index(&self, value: BoundVar) -> &GenericArg<'tcx> { &self.var_values[value] } }
diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index 79c5538..7ad6006 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs
@@ -17,16 +17,16 @@ use crate::infer::InferCtxtBuilder; use crate::infer::{InferCtxt, InferOk, InferResult}; use crate::mir::interpret::ConstValue; -use rustc_data_structures::indexed_vec::Idx; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc_index::vec::Idx; +use rustc_index::vec::IndexVec; use std::fmt::Debug; use syntax_pos::DUMMY_SP; use crate::traits::query::{Fallible, NoSolution}; use crate::traits::TraitEngine; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; use crate::ty::fold::TypeFoldable; -use crate::ty::subst::{Kind, UnpackedKind}; -use crate::ty::{self, BoundVar, InferConst, Ty, TyCtxt}; +use crate::ty::subst::{GenericArg, GenericArgKind}; +use crate::ty::{self, BoundVar, Ty, TyCtxt}; use crate::util::captures::Captures; impl<'tcx> InferCtxtBuilder<'tcx> { @@ -298,11 +298,14 @@ &v.var_values[BoundVar::new(index)] }); match (original_value.unpack(), result_value.unpack()) { - (UnpackedKind::Lifetime(ty::ReErased), UnpackedKind::Lifetime(ty::ReErased)) => { - // no action needed + ( + GenericArgKind::Lifetime(ty::ReErased), + GenericArgKind::Lifetime(ty::ReErased), + ) => { + // No action needed. } - (UnpackedKind::Lifetime(v_o), UnpackedKind::Lifetime(v_r)) => { + (GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => { // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. if v_o != v_r { output_query_region_constraints @@ -314,12 +317,12 @@ } } - (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => { + (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => { let ok = self.at(cause, param_env).eq(v1, v2)?; obligations.extend(ok.into_obligations()); } - (UnpackedKind::Const(v1), UnpackedKind::Const(v2)) => { + (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => { let ok = self.at(cause, param_env).eq(v1, v2)?; obligations.extend(ok.into_obligations()); } @@ -462,16 +465,16 @@ // is directly equal to one of the canonical variables in the // result, then we can type the corresponding value from the // input. See the example above. - let mut opt_values: IndexVec<BoundVar, Option<Kind<'tcx>>> = + let mut opt_values: IndexVec<BoundVar, Option<GenericArg<'tcx>>> = IndexVec::from_elem_n(None, query_response.variables.len()); // In terms of our example above, we are iterating over pairs like: // [(?A, Vec<?0>), ('static, '?1), (?B, ?0)] for (original_value, result_value) in original_values.var_values.iter().zip(result_values) { match result_value.unpack() { - UnpackedKind::Type(result_value) => { + GenericArgKind::Type(result_value) => { // e.g., here `result_value` might be `?0` in the example above... - if let ty::Bound(debruijn, b) = result_value.sty { + if let ty::Bound(debruijn, b) = result_value.kind { // ...in which case we would set `canonical_vars[0]` to `Some(?U)`. // We only allow a `ty::INNERMOST` index in substitutions. @@ -479,7 +482,7 @@ opt_values[b.var] = Some(*original_value); } } - UnpackedKind::Lifetime(result_value) => { + GenericArgKind::Lifetime(result_value) => { // e.g., here `result_value` might be `'?1` in the example above... if let &ty::RegionKind::ReLateBound(debruijn, br) = result_value { // ... in which case we would set `canonical_vars[0]` to `Some('static)`. @@ -489,11 +492,8 @@ opt_values[br.assert_bound_var()] = Some(*original_value); } } - UnpackedKind::Const(result_value) => { - if let ty::Const { - val: ConstValue::Infer(InferConst::Canonical(debrujin, b)), - .. - } = result_value { + GenericArgKind::Const(result_value) => { + if let ty::Const { val: ConstValue::Bound(debrujin, b), .. } = result_value { // ...in which case we would set `canonical_vars[0]` to `Some(const X)`. // We only allow a `ty::INNERMOST` index in substitutions. @@ -553,7 +553,7 @@ // canonical variable; this is taken from // `query_response.var_values` after applying the substitution // `result_subst`. - let substituted_query_response = |index: BoundVar| -> Kind<'tcx> { + let substituted_query_response = |index: BoundVar| -> GenericArg<'tcx> { query_response.substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index]) }; @@ -586,17 +586,17 @@ cause.clone(), param_env, match k1.unpack() { - UnpackedKind::Lifetime(r1) => ty::Predicate::RegionOutlives( + GenericArgKind::Lifetime(r1) => ty::Predicate::RegionOutlives( ty::Binder::bind( ty::OutlivesPredicate(r1, r2) ) ), - UnpackedKind::Type(t1) => ty::Predicate::TypeOutlives( + GenericArgKind::Type(t1) => ty::Predicate::TypeOutlives( ty::Binder::bind( ty::OutlivesPredicate(t1, r2) ) ), - UnpackedKind::Const(..) => { + GenericArgKind::Const(..) => { // Consts cannot outlive one another, so we don't expect to // ecounter this branch. span_bug!(cause.span, "unexpected const outlives {:?}", constraint); @@ -613,7 +613,7 @@ cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, variables1: &OriginalQueryValues<'tcx>, - variables2: impl Fn(BoundVar) -> Kind<'tcx>, + variables2: impl Fn(BoundVar) -> GenericArg<'tcx>, ) -> InferResult<'tcx, ()> { self.commit_if_ok(|_| { let mut obligations = vec![]; @@ -621,21 +621,21 @@ let value2 = variables2(BoundVar::new(index)); match (value1.unpack(), value2.unpack()) { - (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => { + (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => { obligations .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations()); } ( - UnpackedKind::Lifetime(ty::ReErased), - UnpackedKind::Lifetime(ty::ReErased), + GenericArgKind::Lifetime(ty::ReErased), + GenericArgKind::Lifetime(ty::ReErased), ) => { // no action needed } - (UnpackedKind::Lifetime(v1), UnpackedKind::Lifetime(v2)) => { + (GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => { obligations .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations()); } - (UnpackedKind::Const(v1), UnpackedKind::Const(v2)) => { + (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => { let ok = self.at(cause, param_env).eq(v1, v2)?; obligations.extend(ok.into_obligations()); }
diff --git a/src/librustc/infer/canonical/substitute.rs b/src/librustc/infer/canonical/substitute.rs index 1234b96..4f5bb09 100644 --- a/src/librustc/infer/canonical/substitute.rs +++ b/src/librustc/infer/canonical/substitute.rs
@@ -8,7 +8,7 @@ use crate::infer::canonical::{Canonical, CanonicalVarValues}; use crate::ty::fold::TypeFoldable; -use crate::ty::subst::UnpackedKind; +use crate::ty::subst::GenericArgKind; use crate::ty::{self, TyCtxt}; impl<'tcx, V> Canonical<'tcx, V> { @@ -58,21 +58,21 @@ } else { let fld_r = |br: ty::BoundRegion| { match var_values.var_values[br.assert_bound_var()].unpack() { - UnpackedKind::Lifetime(l) => l, + GenericArgKind::Lifetime(l) => l, r => bug!("{:?} is a region but value is {:?}", br, r), } }; let fld_t = |bound_ty: ty::BoundTy| { match var_values.var_values[bound_ty.var].unpack() { - UnpackedKind::Type(ty) => ty, + GenericArgKind::Type(ty) => ty, r => bug!("{:?} is a type but value is {:?}", bound_ty, r), } }; let fld_c = |bound_ct: ty::BoundVar, _| { match var_values.var_values[bound_ct].unpack() { - UnpackedKind::Const(ct) => ct, + GenericArgKind::Const(ct) => ct, c => bug!("{:?} is a const but value is {:?}", bound_ct, c), } };
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 966c581..51ae4e4 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs
@@ -53,7 +53,7 @@ pub obligations: PredicateObligations<'tcx>, } -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +#[derive(Copy, Clone, Debug)] pub enum RelationDir { SubtypeOf, SupertypeOf, EqTo } @@ -70,7 +70,7 @@ { let a_is_expected = relation.a_is_expected(); - match (&a.sty, &b.sty) { + match (&a.kind, &b.kind) { // Relate integral variables to other types (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => { self.int_unification_table @@ -486,7 +486,7 @@ // any other type variable related to `vid` via // subtyping. This is basically our "occurs check", preventing // us from creating infinitely sized types. - match t.sty { + match t.kind { ty::Infer(ty::TyVar(vid)) => { let mut variables = self.infcx.type_variables.borrow_mut(); let vid = variables.root_var(vid); @@ -494,7 +494,7 @@ if sub_vid == self.for_vid_sub_root { // If sub-roots are equal, then `for_vid` and // `vid` are related via subtyping. - return Err(TypeError::CyclicTy(self.root_ty)); + Err(TypeError::CyclicTy(self.root_ty)) } else { match variables.probe(vid) { TypeVariableValue::Known { value: u } => { @@ -527,7 +527,7 @@ let u = self.tcx().mk_ty_var(new_var_id); debug!("generalize: replacing original vid={:?} with new={:?}", vid, u); - return Ok(u); + Ok(u) } } } @@ -602,19 +602,26 @@ ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { assert_eq!(c, c2); // we are abusing TypeRelation here; both LHS and RHS ought to be == - match c { - ty::Const { val: ConstValue::Infer(InferConst::Var(vid)), .. } => { + match c.val { + ConstValue::Infer(InferConst::Var(vid)) => { let mut variable_table = self.infcx.const_unification_table.borrow_mut(); - match variable_table.probe_value(*vid).val.known() { - Some(u) => { - self.relate(&u, &u) + let var_value = variable_table.probe_value(vid); + match var_value.val { + ConstVariableValue::Known { value: u } => self.relate(&u, &u), + ConstVariableValue::Unknown { universe } => { + if self.for_universe.can_name(universe) { + Ok(c) + } else { + let new_var_id = variable_table.new_key(ConstVarValue { + origin: var_value.origin, + val: ConstVariableValue::Unknown { universe: self.for_universe }, + }); + Ok(self.tcx().mk_const_var(new_var_id, c.ty)) + } } - None => Ok(c), } } - _ => { - relate::super_relate_consts(self, c, c) - } + _ => relate::super_relate_consts(self, c, c), } } }
diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs index 6065387..aea58ac 100644 --- a/src/librustc/infer/equate.rs +++ b/src/librustc/infer/equate.rs
@@ -68,7 +68,7 @@ debug!("{}.tys: replacements ({:?}, {:?})", self.tag(), a, b); - match (&a.sty, &b.sty) { + match (&a.kind, &b.kind) { (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => { infcx.type_variables.borrow_mut().equate(a_id, b_id); }
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 0b6740d..498600f 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs
@@ -90,7 +90,7 @@ let span = scope.span(self, region_scope_tree); let tag = match self.hir().find(scope.hir_id(region_scope_tree)) { Some(Node::Block(_)) => "block", - Some(Node::Expr(expr)) => match expr.node { + Some(Node::Expr(expr)) => match expr.kind { hir::ExprKind::Call(..) => "call", hir::ExprKind::MethodCall(..) => "method call", hir::ExprKind::Match(.., hir::MatchSource::IfLetDesugar { .. }) => "if let", @@ -200,7 +200,7 @@ { sp = param.span; } - (format!("the lifetime {} as defined on", br.name), sp) + (format!("the lifetime `{}` as defined on", br.name), sp) } ty::ReFree(ty::FreeRegion { bound_region: ty::BoundRegion::BrNamed(_, name), @@ -213,7 +213,7 @@ { sp = param.span; } - (format!("the lifetime {} as defined on", name), sp) + (format!("the lifetime `{}` as defined on", name), sp) } ty::ReFree(ref fr) => match fr.bound_region { ty::BrAnon(idx) => ( @@ -221,7 +221,7 @@ self.hir().span(node), ), _ => ( - format!("the lifetime {} as defined on", region), + format!("the lifetime `{}` as defined on", region), cm.def_span(self.hir().span(node)), ), }, @@ -248,7 +248,7 @@ } fn item_scope_tag(item: &hir::Item) -> &'static str { - match item.node { + match item.kind { hir::ItemKind::Impl(..) => "impl", hir::ItemKind::Struct(..) => "struct", hir::ItemKind::Union(..) => "union", @@ -260,14 +260,14 @@ } fn trait_item_scope_tag(item: &hir::TraitItem) -> &'static str { - match item.node { + match item.kind { hir::TraitItemKind::Method(..) => "method body", hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => "associated item", } } fn impl_item_scope_tag(item: &hir::ImplItem) -> &'static str { - match item.node { + match item.kind { hir::ImplItemKind::Method(..) => "method body", hir::ImplItemKind::Const(..) | hir::ImplItemKind::OpaqueTy(..) @@ -464,7 +464,7 @@ use hir::def_id::CrateNum; use hir::map::DisambiguatedDefPathData; use ty::print::Printer; - use ty::subst::Kind; + use ty::subst::GenericArg; struct AbsolutePathPrinter<'tcx> { tcx: TyCtxt<'tcx>, @@ -542,13 +542,13 @@ disambiguated_data: &DisambiguatedDefPathData, ) -> Result<Self::Path, Self::Error> { let mut path = print_prefix(self)?; - path.push(disambiguated_data.data.as_interned_str().to_string()); + path.push(disambiguated_data.data.as_symbol().to_string()); Ok(path) } fn path_generic_args( self, print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, - _args: &[Kind<'tcx>], + _args: &[GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { print_prefix(self) } @@ -589,7 +589,7 @@ // if they are both "path types", there's a chance of ambiguity // due to different versions of the same crate if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) - = (&exp_found.expected.sty, &exp_found.found.sty) + = (&exp_found.expected.kind, &exp_found.found.kind) { report_path_match(err, exp_adt.did, found_adt.did); } @@ -639,7 +639,7 @@ hir::MatchSource::TryDesugar => { if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found { let discrim_expr = self.tcx.hir().expect_expr(discrim_hir_id); - let discrim_ty = if let hir::ExprKind::Call(_, args) = &discrim_expr.node { + let discrim_ty = if let hir::ExprKind::Call(_, args) = &discrim_expr.kind { let arg_expr = args.first().expect("try desugaring call w/out arg"); self.in_progress_tables.and_then(|tables| { tables.borrow().expr_ty_opt(arg_expr) @@ -803,7 +803,7 @@ self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); return Some(()); } - if let &ty::Adt(def, _) = &ta.sty { + if let &ty::Adt(def, _) = &ta.kind { let path_ = self.tcx.def_path_str(def.did.clone()); if path_ == other_path { self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); @@ -867,8 +867,11 @@ /// Compares two given types, eliding parts that are the same between them and highlighting /// relevant differences, and return two representation of those types for highlighted printing. fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagnosticStyledString, DiagnosticStyledString) { + debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind, t2, t2.kind); + + // helper functions fn equals<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.sty, &b.sty) { + match (&a.kind, &b.kind) { (a, b) if *a == *b => true, (&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_))) | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Int(_)) @@ -902,7 +905,8 @@ s.push_normal(ty.to_string()); } - match (&t1.sty, &t2.sty) { + // process starts here + match (&t1.kind, &t2.kind) { (&ty::Adt(def1, sub1), &ty::Adt(def2, sub2)) => { let sub_no_defaults_1 = self.strip_generic_default_params(def1.did, sub1); let sub_no_defaults_2 = self.strip_generic_default_params(def2.did, sub2); @@ -935,6 +939,7 @@ .filter(|(a, b)| a == b) .count(); let len = sub1.len() - common_default_params; + let consts_offset = len - sub1.consts().count(); // Only draw `<...>` if there're lifetime/type arguments. if len > 0 { @@ -981,7 +986,8 @@ // ^ elided type as this type argument was the same in both sides let type_arguments = sub1.types().zip(sub2.types()); let regions_len = sub1.regions().count(); - for (i, (ta1, ta2)) in type_arguments.take(len).enumerate() { + let num_display_types = consts_offset - regions_len; + for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() { let i = i + regions_len; if ta1 == ta2 { values.0.push_normal("_"); @@ -994,6 +1000,21 @@ self.push_comma(&mut values.0, &mut values.1, len, i); } + // Do the same for const arguments, if they are equal, do not highlight and + // elide them from the output. + let const_arguments = sub1.consts().zip(sub2.consts()); + for (i, (ca1, ca2)) in const_arguments.enumerate() { + let i = i + consts_offset; + if ca1 == ca2 { + values.0.push_normal("_"); + values.1.push_normal("_"); + } else { + values.0.push_highlighted(ca1.to_string()); + values.1.push_highlighted(ca2.to_string()); + } + self.push_comma(&mut values.0, &mut values.1, len, i); + } + // Close the type argument bracket. // Only draw `<...>` if there're lifetime/type arguments. if len > 0 { @@ -1035,12 +1056,47 @@ return values; } - // We couldn't find anything in common, highlight everything. - // let x: Bar<Qux> = y::<Foo<Zar>>(); - ( - DiagnosticStyledString::highlighted(t1.to_string()), - DiagnosticStyledString::highlighted(t2.to_string()), - ) + // We can't find anything in common, highlight relevant part of type path. + // let x: foo::bar::Baz<Qux> = y:<foo::bar::Bar<Zar>>(); + // foo::bar::Baz<Qux> + // foo::bar::Bar<Zar> + // -------- this part of the path is different + + let t1_str = t1.to_string(); + let t2_str = t2.to_string(); + let min_len = t1_str.len().min(t2_str.len()); + + const SEPARATOR: &str = "::"; + let separator_len = SEPARATOR.len(); + let split_idx: usize = + t1_str.split(SEPARATOR) + .zip(t2_str.split(SEPARATOR)) + .take_while(|(mod1_str, mod2_str)| mod1_str == mod2_str) + .map(|(mod_str, _)| mod_str.len() + separator_len) + .sum(); + + debug!("cmp: separator_len={}, split_idx={}, min_len={}", + separator_len, split_idx, min_len + ); + + if split_idx >= min_len { + // paths are identical, highlight everything + ( + DiagnosticStyledString::highlighted(t1_str), + DiagnosticStyledString::highlighted(t2_str) + ) + } else { + let (common, uniq1) = t1_str.split_at(split_idx); + let (_, uniq2) = t2_str.split_at(split_idx); + debug!("cmp: common={}, uniq1={}, uniq2={}", common, uniq1, uniq2); + + values.0.push_normal(common); + values.0.push_highlighted(uniq1); + values.1.push_normal(common); + values.1.push_highlighted(uniq2); + + values + } } } @@ -1103,6 +1159,7 @@ _ => {} } + debug!("note_type_err(diag={:?})", diag); let (expected_found, exp_found, is_simple_error) = match values { None => (None, None, false), Some(values) => { @@ -1129,16 +1186,23 @@ let span = cause.span(self.tcx); - diag.span_label(span, terr.to_string()); - if let Some((sp, msg)) = secondary_span { - diag.span_label(sp, msg); - } + // Ignore msg for object safe coercion + // since E0038 message will be printed + match terr { + TypeError::ObjectUnsafeCoercion(_) => {} + _ => { + diag.span_label(span, terr.to_string()); + if let Some((sp, msg)) = secondary_span { + diag.span_label(sp, msg); + } + } + }; if let Some((expected, found)) = expected_found { match (terr, is_simple_error, expected == found) { (&TypeError::Sorts(ref values), false, true) => { let sort_string = | a_type: Ty<'tcx> | - if let ty::Opaque(def_id, _) = a_type.sty { + if let ty::Opaque(def_id, _) = a_type.kind { format!(" (opaque type at {})", self.tcx.sess.source_map() .mk_substr_filename(self.tcx.def_span(def_id))) } else { @@ -1152,7 +1216,14 @@ &sort_string(values.found), ); } + (TypeError::ObjectUnsafeCoercion(_), ..) => { + diag.note_unsuccessfull_coercion(found, expected); + } (_, false, _) => { + debug!( + "note_type_err: exp_found={:?}, expected={:?} found={:?}", + exp_found, expected, found + ); if let Some(exp_found) = exp_found { self.suggest_as_ref_where_appropriate(span, &exp_found, diag); } @@ -1179,9 +1250,9 @@ exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, diag: &mut DiagnosticBuilder<'tcx>, ) { - match (&exp_found.expected.sty, &exp_found.found.sty) { + match (&exp_found.expected.kind, &exp_found.found.kind) { (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) => { - if let ty::Adt(found_def, found_substs) = found_ty.sty { + if let ty::Adt(found_def, found_substs) = found_ty.kind { let path_str = format!("{:?}", exp_def); if exp_def == &found_def { let opt_msg = "you can convert from `&Option<T>` to `Option<&T>` using \ @@ -1203,9 +1274,9 @@ { let mut show_suggestion = true; for (exp_ty, found_ty) in exp_substs.types().zip(found_substs.types()) { - match exp_ty.sty { + match exp_ty.kind { ty::Ref(_, exp_ty, _) => { - match (&exp_ty.sty, &found_ty.sty) { + match (&exp_ty.kind, &found_ty.kind) { (_, ty::Param(_)) | (_, ty::Infer(_)) | (ty::Param(_), _) | @@ -1250,6 +1321,10 @@ let span = trace.cause.span(self.tcx); let failure_code = trace.cause.as_failure_code(terr); let mut diag = match failure_code { + FailureCode::Error0038(did) => { + let violations = self.tcx.object_safety_violations(did); + self.tcx.report_object_safety_error(span, did, violations) + } FailureCode::Error0317(failure_str) => { struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str) } @@ -1611,6 +1686,7 @@ } enum FailureCode { + Error0038(DefId), Error0317(&'static str), Error0580(&'static str), Error0308(&'static str), @@ -1649,6 +1725,7 @@ TypeError::IntrinsicCast => { Error0308("cannot coerce intrinsics to function pointers") } + TypeError::ObjectUnsafeCoercion(did) => Error0038(did.clone()), _ => Error0308("mismatched types"), }, }
diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs index 7068fe3..b897312 100644 --- a/src/librustc/infer/error_reporting/need_type_info.rs +++ b/src/librustc/infer/error_reporting/need_type_info.rs
@@ -44,7 +44,7 @@ Some(ty) => { let ty = self.infcx.resolve_vars_if_possible(&ty); if ty.walk().any(|inner_ty| { - inner_ty == self.target_ty || match (&inner_ty.sty, &self.target_ty.sty) { + inner_ty == self.target_ty || match (&inner_ty.kind, &self.target_ty.kind) { (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => { self.infcx .type_variables @@ -92,10 +92,10 @@ fn visit_expr(&mut self, expr: &'tcx Expr) { if let (ExprKind::Closure(_, _fn_decl, _id, _sp, _), Some(_)) = ( - &expr.node, + &expr.kind, self.node_matches_type(expr.hir_id), ) { - self.found_closure = Some(&expr.node); + self.found_closure = Some(&expr.kind); } intravisit::walk_expr(self, expr); } @@ -114,7 +114,7 @@ FunctionRetTy::DefaultReturn(_) => ("-> ", " "), _ => ("", ""), }; - let suggestion = match body.value.node { + let suggestion = match body.value.kind { ExprKind::Block(..) => { vec![(output.span(), format!("{}{}{}", arrow, ret, post))] } @@ -151,7 +151,7 @@ ty: Ty<'tcx>, highlight: Option<ty::print::RegionHighlightMode>, ) -> (String, Option<Span>) { - if let ty::Infer(ty::TyVar(ty_vid)) = ty.sty { + if let ty::Infer(ty::TyVar(ty_vid)) = ty.kind { let ty_vars = self.type_variables.borrow(); let var_origin = ty_vars.var_origin(ty_vid); if let TypeVariableOriginKind::TypeParameterDefinition(name) = var_origin.kind { @@ -219,8 +219,8 @@ }; let ty_msg = match local_visitor.found_ty { - Some(ty::TyS { sty: ty::Closure(def_id, substs), .. }) => { - let fn_sig = substs.closure_sig(*def_id, self.tcx); + Some(ty::TyS { kind: ty::Closure(def_id, substs), .. }) => { + let fn_sig = substs.as_closure().sig(*def_id, self.tcx); let args = closure_args(&fn_sig); let ret = fn_sig.output().skip_binder().to_string(); format!(" for the closure `fn({}) -> {}`", args, ret) @@ -254,8 +254,8 @@ ); let suffix = match local_visitor.found_ty { - Some(ty::TyS { sty: ty::Closure(def_id, substs), .. }) => { - let fn_sig = substs.closure_sig(*def_id, self.tcx); + Some(ty::TyS { kind: ty::Closure(def_id, substs), .. }) => { + let fn_sig = substs.as_closure().sig(*def_id, self.tcx); let ret = fn_sig.output().skip_binder().to_string(); if let Some(ExprKind::Closure(_, decl, body_id, ..)) = local_visitor.found_closure {
diff --git a/src/librustc/infer/error_reporting/nice_region_error/find_anon_type.rs b/src/librustc/infer/error_reporting/nice_region_error/find_anon_type.rs index 34f3b8a..f4751e5 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/find_anon_type.rs
@@ -31,15 +31,15 @@ if let Some(hir_id) = self.tcx().hir().as_local_hir_id(def_id) { let fndecl = match self.tcx().hir().get(hir_id) { Node::Item(&hir::Item { - node: hir::ItemKind::Fn(ref fndecl, ..), + kind: hir::ItemKind::Fn(ref fndecl, ..), .. }) => &fndecl, Node::TraitItem(&hir::TraitItem { - node: hir::TraitItemKind::Method(ref m, ..), + kind: hir::TraitItemKind::Method(ref m, ..), .. }) | Node::ImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Method(ref m, ..), + kind: hir::ImplItemKind::Method(ref m, ..), .. }) => &m.decl, _ => return None, @@ -62,7 +62,7 @@ &self, arg: &'tcx hir::Ty, br: &ty::BoundRegion, - ) -> Option<(&'tcx hir::Ty)> { + ) -> Option<&'tcx hir::Ty> { let mut nested_visitor = FindNestedTypeVisitor { tcx: self.tcx(), bound_region: *br, @@ -98,7 +98,7 @@ } fn visit_ty(&mut self, arg: &'tcx hir::Ty) { - match arg.node { + match arg.kind { hir::TyKind::BareFn(_) => { self.current_index.shift_in(1); intravisit::walk_ty(self, arg);
diff --git a/src/librustc/infer/error_reporting/nice_region_error/mod.rs b/src/librustc/infer/error_reporting/nice_region_error/mod.rs index 1edb1c6..cd003aa 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/mod.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/mod.rs
@@ -12,6 +12,7 @@ mod placeholder_error; mod outlives_closure; mod static_impl_trait; +mod trait_impl_difference; mod util; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { @@ -73,6 +74,7 @@ .or_else(|| self.try_report_anon_anon_conflict()) .or_else(|| self.try_report_outlives_closure()) .or_else(|| self.try_report_static_impl_trait()) + .or_else(|| self.try_report_impl_not_conforming_to_trait()) } pub fn get_regions(&self) -> (Span, ty::Region<'tcx>, ty::Region<'tcx>) {
diff --git a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs index 604115c..a9a2c15 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs
@@ -87,7 +87,7 @@ return None; } if let FunctionRetTy::Return(ty) = &fndecl.output { - if let (TyKind::Def(_, _), ty::ReStatic) = (&ty.node, sub) { + if let (TyKind::Def(_, _), ty::ReStatic) = (&ty.kind, sub) { // This is an impl Trait return that evaluates de need of 'static. // We handle this case better in `static_impl_trait`. return None;
diff --git a/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs b/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs index f5a4dac..9231e4f 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs
@@ -50,7 +50,7 @@ let hir = &self.tcx().hir(); if let Some(hir_id) = hir.as_local_hir_id(free_region.scope) { if let Node::Expr(Expr { - node: Closure(_, _, _, closure_span, None), + kind: Closure(_, _, _, closure_span, None), .. }) = hir.get(hir_id) { let sup_sp = sup_origin.span();
diff --git a/src/librustc/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc/infer/error_reporting/nice_region_error/trait_impl_difference.rs new file mode 100644 index 0000000..0194300 --- /dev/null +++ b/src/librustc/infer/error_reporting/nice_region_error/trait_impl_difference.rs
@@ -0,0 +1,59 @@ +//! Error Reporting for `impl` items that do not match the obligations from their `trait`. + +use syntax_pos::Span; +use crate::ty::Ty; +use crate::infer::{ValuePairs, Subtype}; +use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::util::common::ErrorReported; +use crate::traits::ObligationCauseCode::CompareImplMethodObligation; + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`. + pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorReported> { + if let Some(ref error) = self.error { + debug!("try_report_impl_not_conforming_to_trait {:?}", error); + if let RegionResolutionError::SubSupConflict( + _, + var_origin, + sub_origin, + _sub, + sup_origin, + _sup, + ) = error.clone() { + match (&sup_origin, &sub_origin) { + (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) => { + if let ( + ValuePairs::Types(sub_expected_found), + ValuePairs::Types(sup_expected_found), + CompareImplMethodObligation { trait_item_def_id, .. }, + ) = (&sub_trace.values, &sup_trace.values, &sub_trace.cause.code) { + if sup_expected_found == sub_expected_found { + self.emit_err( + var_origin.span(), + sub_expected_found.expected, + sub_expected_found.found, + self.tcx().def_span(*trait_item_def_id), + ); + return Some(ErrorReported); + } + } + } + _ => {} + } + } + } + None + } + + fn emit_err(&self, sp: Span, expected: Ty<'tcx>, found: Ty<'tcx>, impl_sp: Span) { + let mut err = self.tcx().sess.struct_span_err( + sp, + "`impl` item signature doesn't match `trait` item signature", + ); + err.note(&format!("expected `{:?}`\n found `{:?}`", expected, found)); + err.span_label(sp, &format!("found {:?}", found)); + err.span_label(impl_sp, &format!("expected {:?}", expected)); + err.emit(); + } +}
diff --git a/src/librustc/infer/error_reporting/nice_region_error/util.rs b/src/librustc/infer/error_reporting/nice_region_error/util.rs index 668c99d..a2e48cf 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/util.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/util.rs
@@ -109,7 +109,7 @@ decl: &hir::FnDecl, ) -> Option<Span> { let ret_ty = self.tcx().type_of(scope_def_id); - if let ty::FnDef(_, _) = ret_ty.sty { + if let ty::FnDef(_, _) = ret_ty.kind { let sig = ret_ty.fn_sig(self.tcx()); let late_bound_regions = self.tcx() .collect_referenced_late_bound_regions(&sig.output());
diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index 400a538..1841bd9 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs
@@ -153,7 +153,7 @@ let tcx = self.infcx.tcx; - match t.sty { + match t.kind { ty::Infer(ty::TyVar(v)) => { let opt_ty = self.infcx.type_variables.borrow_mut().probe(v).known(); self.freshen_ty( @@ -252,7 +252,7 @@ return ct; } - ConstValue::Infer(ty::InferConst::Canonical(..)) | + ConstValue::Bound(..) | ConstValue::Placeholder(_) => { bug!("unexpected const {:?}", ct) }
diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs index 658a9c1..e27766f 100644 --- a/src/librustc/infer/fudge.rs +++ b/src/librustc/infer/fudge.rs
@@ -148,7 +148,7 @@ } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.sty { + match ty.kind { ty::Infer(ty::InferTy::TyVar(vid)) => { if self.type_vars.0.contains(&vid) { // This variable was created during the fudging.
diff --git a/src/librustc/infer/lattice.rs b/src/librustc/infer/lattice.rs index 68cbef4..3970123 100644 --- a/src/librustc/infer/lattice.rs +++ b/src/librustc/infer/lattice.rs
@@ -61,7 +61,7 @@ let infcx = this.infcx(); let a = infcx.type_variables.borrow_mut().replace_if_possible(a); let b = infcx.type_variables.borrow_mut().replace_if_possible(b); - match (&a.sty, &b.sty) { + match (&a.kind, &b.kind) { // If one side is known to be a variable and one is not, // create a variable (`v`) to represent the LUB. Make sure to // relate `v` to the non-type-variable first (by passing it
diff --git a/src/librustc/infer/lexical_region_resolve/README.md b/src/librustc/infer/lexical_region_resolve/README.md index 7eb4da8..c26b562 100644 --- a/src/librustc/infer/lexical_region_resolve/README.md +++ b/src/librustc/infer/lexical_region_resolve/README.md
@@ -1,268 +1,7 @@ -# Region inference -> WARNING: This README is obsolete and will be removed soon! For -> more info on how the current borrowck works, see the [rustc guide]. -> -> As of edition 2018, region inference is done using Non-lexical lifetimes, -> which is described in the guide and [this RFC]. +Lexical Region Resolution was removed in https://github.com/rust-lang/rust/pull/64790. -[rustc guide]: https://rust-lang.github.io/rustc-guide/borrow_check/region_inference.html -[this RFC]: https://github.com/rust-lang/rfcs/blob/master/text/2094-nll.md +Rust now uses Non-lexical lifetimes. For more info, please see the [borrowck +chapter][bc] in the rustc-guide. -## Terminology - -Note that we use the terms region and lifetime interchangeably. - -## Introduction - -Region inference uses a somewhat more involved algorithm than type -inference. It is not the most efficient thing ever written though it -seems to work well enough in practice (famous last words). The reason -that we use a different algorithm is because, unlike with types, it is -impractical to hand-annotate with regions (in some cases, there aren't -even the requisite syntactic forms). So we have to get it right, and -it's worth spending more time on a more involved analysis. Moreover, -regions are a simpler case than types: they don't have aggregate -structure, for example. - -## The problem - -Basically our input is a directed graph where nodes can be divided -into two categories: region variables and concrete regions. Each edge -`R -> S` in the graph represents a constraint that the region `R` is a -subregion of the region `S`. - -Region variable nodes can have arbitrary degree. There is one region -variable node per region variable. - -Each concrete region node is associated with some, well, concrete -region: e.g., a free lifetime, or the region for a particular scope. -Note that there may be more than one concrete region node for a -particular region value. Moreover, because of how the graph is built, -we know that all concrete region nodes have either in-degree 1 or -out-degree 1. - -Before resolution begins, we build up the constraints in a hashmap -that maps `Constraint` keys to spans. During resolution, we construct -the actual `Graph` structure that we describe here. - -## Computing the values for region variables - -The algorithm is a simple dataflow algorithm. Each region variable -begins as empty. We iterate over the constraints, and for each constraint -we grow the relevant region variable to be as big as it must be to meet all the -constraints. This means the region variables can grow to be `'static` if -necessary. - -## Verification - -After all constraints are fully propoagated, we do a "verification" -step where we walk over the verify bounds and check that they are -satisfied. These bounds represent the "maximal" values that a region -variable can take on, basically. - -## The Region Hierarchy - -### Without closures - -Let's first consider the region hierarchy without thinking about -closures, because they add a lot of complications. The region -hierarchy *basically* mirrors the lexical structure of the code. -There is a region for every piece of 'evaluation' that occurs, meaning -every expression, block, and pattern (patterns are considered to -"execute" by testing the value they are applied to and creating any -relevant bindings). So, for example: - -```rust -fn foo(x: isize, y: isize) { // -+ -// +------------+ // | -// | +-----+ // | -// | +-+ +-+ +-+ // | -// | | | | | | | // | -// v v v v v v v // | - let z = x + y; // | - ... // | -} // -+ - -fn bar() { ... } -``` - -In this example, there is a region for the fn body block as a whole, -and then a subregion for the declaration of the local variable. -Within that, there are sublifetimes for the assignment pattern and -also the expression `x + y`. The expression itself has sublifetimes -for evaluating `x` and `y`. - -#s## Function calls - -Function calls are a bit tricky. I will describe how we handle them -*now* and then a bit about how we can improve them (Issue #6268). - -Consider a function call like `func(expr1, expr2)`, where `func`, -`arg1`, and `arg2` are all arbitrary expressions. Currently, -we construct a region hierarchy like: - - +----------------+ - | | - +--+ +---+ +---+| - v v v v v vv - func(expr1, expr2) - -Here you can see that the call as a whole has a region and the -function plus arguments are subregions of that. As a side-effect of -this, we get a lot of spurious errors around nested calls, in -particular when combined with `&mut` functions. For example, a call -like this one - -```rust -self.foo(self.bar()) -``` - -where both `foo` and `bar` are `&mut self` functions will always yield -an error. - -Here is a more involved example (which is safe) so we can see what's -going on: - -```rust -struct Foo { f: usize, g: usize } -// ... -fn add(p: &mut usize, v: usize) { - *p += v; -} -// ... -fn inc(p: &mut usize) -> usize { - *p += 1; *p -} -fn weird() { - let mut x: Box<Foo> = box Foo { /* ... */ }; - 'a: add(&mut (*x).f, - 'b: inc(&mut (*x).f)) // (..) -} -``` - -The important part is the line marked `(..)` which contains a call to -`add()`. The first argument is a mutable borrow of the field `f`. The -second argument also borrows the field `f`. Now, in the current borrow -checker, the first borrow is given the lifetime of the call to -`add()`, `'a`. The second borrow is given the lifetime of `'b` of the -call to `inc()`. Because `'b` is considered to be a sublifetime of -`'a`, an error is reported since there are two co-existing mutable -borrows of the same data. - -However, if we were to examine the lifetimes a bit more carefully, we -can see that this error is unnecessary. Let's examine the lifetimes -involved with `'a` in detail. We'll break apart all the steps involved -in a call expression: - -```rust -'a: { - 'a_arg1: let a_temp1: ... = add; - 'a_arg2: let a_temp2: &'a mut usize = &'a mut (*x).f; - 'a_arg3: let a_temp3: usize = { - let b_temp1: ... = inc; - let b_temp2: &'b = &'b mut (*x).f; - 'b_call: b_temp1(b_temp2) - }; - 'a_call: a_temp1(a_temp2, a_temp3) // (**) -} -``` - -Here we see that the lifetime `'a` includes a number of substatements. -In particular, there is this lifetime I've called `'a_call` that -corresponds to the *actual execution of the function `add()`*, after -all arguments have been evaluated. There is a corresponding lifetime -`'b_call` for the execution of `inc()`. If we wanted to be precise -about it, the lifetime of the two borrows should be `'a_call` and -`'b_call` respectively, since the references that were created -will not be dereferenced except during the execution itself. - -However, this model by itself is not sound. The reason is that -while the two references that are created will never be used -simultaneously, it is still true that the first reference is -*created* before the second argument is evaluated, and so even though -it will not be *dereferenced* during the evaluation of the second -argument, it can still be *invalidated* by that evaluation. Consider -this similar but unsound example: - -```rust -struct Foo { f: usize, g: usize } -// ... -fn add(p: &mut usize, v: usize) { - *p += v; -} -// ... -fn consume(x: Box<Foo>) -> usize { - x.f + x.g -} -fn weird() { - let mut x: Box<Foo> = box Foo { ... }; - 'a: add(&mut (*x).f, consume(x)) // (..) -} -``` - -In this case, the second argument to `add` actually consumes `x`, thus -invalidating the first argument. - -So, for now, we exclude the `call` lifetimes from our model. -Eventually I would like to include them, but we will have to make the -borrow checker handle this situation correctly. In particular, if -there is a reference created whose lifetime does not enclose -the borrow expression, we must issue sufficient restrictions to ensure -that the pointee remains valid. - -### Modeling closures - -Integrating closures properly into the model is a bit of -work-in-progress. In an ideal world, we would model closures as -closely as possible after their desugared equivalents. That is, a -closure type would be modeled as a struct, and the region hierarchy of -different closure bodies would be completely distinct from all other -fns. We are generally moving in that direction but there are -complications in terms of the implementation. - -In practice what we currently do is somewhat different. The basis for -the current approach is the observation that the only time that -regions from distinct fn bodies interact with one another is through -an upvar or the type of a fn parameter (since closures live in the fn -body namespace, they can in fact have fn parameters whose types -include regions from the surrounding fn body). For these cases, there -are separate mechanisms which ensure that the regions that appear in -upvars/parameters outlive the dynamic extent of each call to the -closure: - -1. Types must outlive the region of any expression where they are used. - For a closure type `C` to outlive a region `'r`, that implies that the - types of all its upvars must outlive `'r`. -2. Parameters must outlive the region of any fn that they are passed to. - -Therefore, we can -- sort of -- assume that any region from an -enclosing fns is larger than any region from one of its enclosed -fn. And that is precisely what we do: when building the region -hierarchy, each region lives in its own distinct subtree, but if we -are asked to compute the `LUB(r1, r2)` of two regions, and those -regions are in disjoint subtrees, we compare the lexical nesting of -the two regions. - -*Ideas for improving the situation:* (FIXME #3696) The correctness -argument here is subtle and a bit hand-wavy. The ideal, as stated -earlier, would be to model things in such a way that it corresponds -more closely to the desugared code. The best approach for doing this -is a bit unclear: it may in fact be possible to *actually* desugar -before we start, but I don't think so. The main option that I've been -thinking through is imposing a "view shift" as we enter the fn body, -so that regions appearing in the types of fn parameters and upvars are -translated from being regions in the outer fn into free region -parameters, just as they would be if we applied the desugaring. The -challenge here is that type inference may not have fully run, so the -types may not be fully known: we could probably do this translation -lazilly, as type variables are instantiated. We would also have to -apply a kind of inverse translation to the return value. This would be -a good idea anyway, as right now it is possible for free regions -instantiated within the closure to leak into the parent: this -currently leads to type errors, since those regions cannot outlive any -expressions within the parent hierarchy. Much like the current -handling of closures, there are no known cases where this leads to a -type-checking accepting incorrect code (though it sometimes rejects -what might be considered correct code; see rust-lang/rust#22557), but -it still doesn't feel like the right approach. +[bc]: https://rust-lang.github.io/rustc-guide/borrow_check/region_inference.html
diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 6282fde..f30f19d 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs
@@ -19,8 +19,8 @@ use rustc_data_structures::graph::implementation::{ Direction, Graph, NodeIndex, INCOMING, OUTGOING, }; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use smallvec::SmallVec; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::{Idx, IndexVec}; use std::fmt; use syntax_pos::Span; @@ -304,8 +304,7 @@ } fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) { - self.iterate_until_fixed_point("Expansion", |constraint| { - debug!("expansion: constraint={:?}", constraint); + let mut process_constraint = |constraint: &Constraint<'tcx>| { let (a_region, b_vid, b_data, retain) = match *constraint { Constraint::RegSubVar(a_region, b_vid) => { let b_data = var_values.value_mut(b_vid); @@ -331,7 +330,33 @@ let changed = self.expand_node(a_region, b_vid, b_data); (changed, retain) - }) + }; + + // Using bitsets to track the remaining elements is faster than using a + // `Vec` by itself (which requires removing elements, which requires + // element shuffling, which is slow). + let constraints: Vec<_> = self.data.constraints.keys().collect(); + let mut live_indices: BitSet<usize> = BitSet::new_filled(constraints.len()); + let mut killed_indices: BitSet<usize> = BitSet::new_empty(constraints.len()); + let mut changed = true; + while changed { + changed = false; + for index in live_indices.iter() { + let constraint = constraints[index]; + let (edge_changed, retain) = process_constraint(constraint); + if edge_changed { + changed = true; + } + if !retain { + let changed = killed_indices.insert(index); + debug_assert!(changed); + } + } + live_indices.subtract(&killed_indices); + + // We could clear `killed_indices` here, but we don't need to and + // it's cheaper not to. + } } // This function is very hot in some workloads. There's a single callsite @@ -360,13 +385,21 @@ match *b_data { VarValue::Value(cur_region) => { // Identical scopes can show up quite often, if the fixed point - // iteration converges slowly, skip them + // iteration converges slowly. Skip them. This is purely an + // optimization. if let (ReScope(a_scope), ReScope(cur_scope)) = (a_region, cur_region) { if a_scope == cur_scope { return false; } } + // This is a specialized version of the `lub_concrete_regions` + // check below for a common case, here purely as an + // optimization. + if let ReEmpty = a_region { + return false; + } + let mut lub = self.lub_concrete_regions(a_region, cur_region); if lub == cur_region { return false; @@ -407,8 +440,6 @@ /// Returns the smallest region `c` such that `a <= c` and `b <= c`. fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { - let tcx = self.tcx(); - match (a, b) { (&ty::ReClosureBound(..), _) | (_, &ty::ReClosureBound(..)) @@ -468,7 +499,7 @@ // otherwise, we don't know what the free region is, // so we must conservatively say the LUB is static: - tcx.lifetimes.re_static + self.tcx().lifetimes.re_static } (&ReScope(a_id), &ReScope(b_id)) => { @@ -476,7 +507,7 @@ // subtype of the region corresponding to an inner // block. let lub = self.region_rels.region_scope_tree.nearest_common_ancestor(a_id, b_id); - tcx.mk_region(ReScope(lub)) + self.tcx().mk_region(ReScope(lub)) } (&ReEarlyBound(_), &ReEarlyBound(_)) @@ -490,7 +521,7 @@ if a == b { a } else { - tcx.lifetimes.re_static + self.tcx().lifetimes.re_static } } } @@ -860,29 +891,6 @@ } } - fn iterate_until_fixed_point<F>(&self, tag: &str, mut body: F) - where - F: FnMut(&Constraint<'tcx>) -> (bool, bool), - { - let mut constraints: SmallVec<[_; 16]> = self.data.constraints.keys().collect(); - let mut iteration = 0; - let mut changed = true; - while changed { - changed = false; - iteration += 1; - debug!("---- {} Iteration {}{}", "#", tag, iteration); - constraints.retain(|constraint| { - let (edge_changed, retain) = body(constraint); - if edge_changed { - debug!("updated due to constraint {:?}", constraint); - changed = true; - } - retain - }); - } - debug!("---- {} Complete after {} iteration(s)", tag, iteration); - } - fn bound_is_met( &self, bound: &VerifyBound<'tcx>,
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index c5712cc..e385d57 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs
@@ -20,10 +20,10 @@ use crate::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; use crate::ty::fold::{TypeFolder, TypeFoldable}; use crate::ty::relate::RelateResult; -use crate::ty::subst::{Kind, InternalSubsts, SubstsRef}; +use crate::ty::subst::{GenericArg, InternalSubsts, SubstsRef}; use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt, InferConst}; use crate::ty::{FloatVid, IntVid, TyVid, ConstVid}; -use crate::util::nodemap::FxHashMap; +use crate::util::nodemap::{FxHashMap, FxHashSet}; use errors::DiagnosticBuilder; use rustc_data_structures::sync::Lrc; @@ -32,7 +32,7 @@ use std::collections::BTreeMap; use std::fmt; use syntax::ast; -use syntax_pos::symbol::InternedString; +use syntax_pos::symbol::Symbol; use syntax_pos::Span; use self::combine::CombineFields; @@ -93,6 +93,8 @@ /// checks, so we should ignore errors if NLL is (unconditionally) /// enabled. pub fn when_nll_is_enabled(tcx: TyCtxt<'_>) -> Self { + // FIXME(Centril): Once we actually remove `::Migrate` also make + // this always `true` and then proceed to eliminate the dead code. match tcx.borrowck_mode() { // If we're on Migrate mode, report AST region errors BorrowckMode::Migrate => SuppressRegionErrors { suppressed: false }, @@ -153,6 +155,8 @@ /// avoid reporting the same error twice. pub reported_trait_errors: RefCell<FxHashMap<Span, Vec<ty::Predicate<'tcx>>>>, + pub reported_closure_mismatch: RefCell<FxHashSet<(Span, Option<Span>)>>, + /// When an error occurs, we want to avoid reporting "derived" /// errors that are due to this original failure. Normally, we /// handle this with the `err_count_on_creation` count, which @@ -388,7 +392,7 @@ Coercion(Span), /// Region variables created as the values for early-bound regions - EarlyBoundRegion(Span, InternedString), + EarlyBoundRegion(Span, Symbol), /// Region variables created for bound regions /// in a function or method that is called @@ -403,7 +407,7 @@ NLL(NLLRegionVariableOrigin), } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug)] pub enum NLLRegionVariableOrigin { /// During NLL region processing, we create variables for free /// regions that we encounter in the function signature and @@ -414,7 +418,19 @@ /// from a `for<'a> T` binder). Meant to represent "any region". Placeholder(ty::PlaceholderRegion), - Existential, + Existential { + /// If this is true, then this variable was created to represent a lifetime + /// bound in a `for` binder. For example, it might have been created to + /// represent the lifetime `'a` in a type like `for<'a> fn(&'a u32)`. + /// Such variables are created when we are trying to figure out if there + /// is any valid instantiation of `'a` that could fit into some scenario. + /// + /// This is used to inform error reporting: in the case that we are trying to + /// determine whether there is any valid instantiation of a `'a` variable that meets + /// some constraint C, we want to blame the "source" of that `for` type, + /// rather than blaming the source of the constraint C. + from_forall: bool + }, } impl NLLRegionVariableOrigin { @@ -422,7 +438,7 @@ match self { NLLRegionVariableOrigin::FreeRegion => true, NLLRegionVariableOrigin::Placeholder(..) => true, - NLLRegionVariableOrigin::Existential => false, + NLLRegionVariableOrigin::Existential{ .. } => false, } } @@ -536,6 +552,7 @@ selection_cache: Default::default(), evaluation_cache: Default::default(), reported_trait_errors: Default::default(), + reported_closure_mismatch: Default::default(), tainted_by_errors_flag: Cell::new(false), err_count_on_creation: tcx.sess.err_count(), in_snapshot: Cell::new(false), @@ -614,7 +631,7 @@ } pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> bool { - match ty.sty { + match ty.kind { ty::Infer(ty::TyVar(vid)) => self.type_variables.borrow().var_diverges(vid), _ => false, } @@ -627,7 +644,7 @@ pub fn type_is_unconstrained_numeric(&'a self, ty: Ty<'_>) -> UnconstrainedNumeric { use crate::ty::error::UnconstrainedNumeric::Neither; use crate::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; - match ty.sty { + match ty.kind { ty::Infer(ty::IntVar(vid)) => { if self.int_unification_table .borrow_mut() @@ -797,16 +814,16 @@ /// Executes `f` and commit the bindings. pub fn commit_unconditionally<R, F>(&self, f: F) -> R where - F: FnOnce() -> R, + F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R, { - debug!("commit()"); + debug!("commit_unconditionally()"); let snapshot = self.start_snapshot(); - let r = f(); + let r = f(&snapshot); self.commit_from(snapshot); r } - /// Executes `f` and commit the bindings if closure `f` returns `Ok(_)`. + /// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`. pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E> where F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> Result<T, E>, @@ -826,19 +843,7 @@ r } - /// Execute `f` in a snapshot, and commit the bindings it creates. - pub fn in_snapshot<T, F>(&self, f: F) -> T - where - F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> T, - { - debug!("in_snapshot()"); - let snapshot = self.start_snapshot(); - let r = f(&snapshot); - self.commit_from(snapshot); - r - } - - /// Executes `f` then unroll any bindings it creates. + /// Execute `f` then unroll any bindings it creates. pub fn probe<R, F>(&self, f: F) -> R where F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R, @@ -1110,7 +1115,7 @@ self.next_region_var_in_universe(RegionVariableOrigin::NLL(origin), universe) } - pub fn var_for_def(&self, span: Span, param: &ty::GenericParamDef) -> Kind<'tcx> { + pub fn var_for_def(&self, span: Span, param: &ty::GenericParamDef) -> GenericArg<'tcx> { match param.kind { GenericParamDefKind::Lifetime => { // Create a region inference variable for the given @@ -1302,6 +1307,14 @@ } } + /// Resolve any type variables found in `value` -- but only one + /// level. So, if the variable `?X` is bound to some type + /// `Foo<?Y>`, then this would return `Foo<?Y>` (but `?Y` may + /// itself be bound to a type). + /// + /// Useful when you only need to inspect the outermost level of + /// the type and don't care about nested types (or perhaps you + /// will be resolving them as well, e.g. in a loop). pub fn shallow_resolve<T>(&self, value: T) -> T where T: TypeFoldable<'tcx>, @@ -1460,7 +1473,7 @@ // type-checking closure types are in local tables only. if !self.in_progress_tables.is_some() || !ty.has_closure_types() { if !(param_env, ty).has_local_value() { - return ty.is_copy_modulo_regions(self.tcx.global_tcx(), param_env, span); + return ty.is_copy_modulo_regions(self.tcx, param_env, span); } } @@ -1479,9 +1492,9 @@ pub fn closure_kind( &self, closure_def_id: DefId, - closure_substs: ty::ClosureSubsts<'tcx>, + closure_substs: SubstsRef<'tcx>, ) -> Option<ty::ClosureKind> { - let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self.tcx); + let closure_kind_ty = closure_substs.as_closure().kind_ty(closure_def_id, self.tcx); let closure_kind_ty = self.shallow_resolve(closure_kind_ty); closure_kind_ty.to_opt_closure_kind() } @@ -1493,9 +1506,9 @@ pub fn closure_sig( &self, def_id: DefId, - substs: ty::ClosureSubsts<'tcx>, + substs: SubstsRef<'tcx>, ) -> ty::PolyFnSig<'tcx> { - let closure_sig_ty = substs.closure_sig_ty(def_id, self.tcx); + let closure_sig_ty = substs.as_closure().sig_ty(def_id, self.tcx); let closure_sig_ty = self.shallow_resolve(closure_sig_ty); closure_sig_ty.fn_sig(self.tcx) } @@ -1562,8 +1575,11 @@ ShallowResolver { infcx } } + /// If `typ` is a type variable of some kind, resolve it one level + /// (but do not resolve types found in the result). If `typ` is + /// not a type variable, just return it unmodified. pub fn shallow_resolve(&mut self, typ: Ty<'tcx>) -> Ty<'tcx> { - match typ.sty { + match typ.kind { ty::Infer(ty::TyVar(v)) => { // Not entirely obvious: if `typ` is a type variable, // it can be resolved to an int/float variable, which @@ -1600,29 +1616,30 @@ // `resolver.shallow_resolve_changed(ty)` is equivalent to // `resolver.shallow_resolve(ty) != ty`, but more efficient. It's always - // inlined, despite being large, because it has a single call site that is - // extremely hot. + // inlined, despite being large, because it has only two call sites that + // are extremely hot. #[inline(always)] pub fn shallow_resolve_changed(&mut self, typ: Ty<'tcx>) -> bool { - match typ.sty { + match typ.kind { ty::Infer(ty::TyVar(v)) => { use self::type_variable::TypeVariableValue; // See the comment in `shallow_resolve()`. - match self.infcx.type_variables.borrow_mut().probe(v) { + match self.infcx.type_variables.borrow_mut().inlined_probe(v) { TypeVariableValue::Known { value: t } => self.fold_ty(t) != typ, TypeVariableValue::Unknown { .. } => false, } } ty::Infer(ty::IntVar(v)) => { - match self.infcx.int_unification_table.borrow_mut().probe_value(v) { + match self.infcx.int_unification_table.borrow_mut().inlined_probe_value(v) { Some(v) => v.to_type(self.infcx.tcx) != typ, None => false, } } ty::Infer(ty::FloatVar(v)) => { + // Not `inlined_probe_value(v)` because this call site is colder. match self.infcx.float_unification_table.borrow_mut().probe_value(v) { Some(v) => v.to_type(self.infcx.tcx) != typ, None => false,
diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index 5d521de..d6f76e9 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs
@@ -26,13 +26,14 @@ use crate::ty::error::TypeError; use crate::ty::fold::{TypeFoldable, TypeVisitor}; use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use crate::ty::subst::Kind; +use crate::ty::subst::GenericArg; use crate::ty::{self, Ty, TyCtxt, InferConst}; +use crate::infer::{ConstVariableValue, ConstVarValue}; use crate::mir::interpret::ConstValue; use rustc_data_structures::fx::FxHashMap; use std::fmt::Debug; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq)] pub enum NormalizationStrategy { Lazy, Eager, @@ -93,7 +94,7 @@ /// we will invoke this method to instantiate `'a` with an /// inference variable (though `'b` would be instantiated first, /// as a placeholder). - fn next_existential_region_var(&mut self) -> ty::Region<'tcx>; + fn next_existential_region_var(&mut self, was_placeholder: bool) -> ty::Region<'tcx>; /// Creates a new region variable representing a /// higher-ranked region that is instantiated universally. @@ -124,7 +125,7 @@ #[derive(Clone, Debug)] struct ScopesAndKind<'tcx> { scopes: Vec<BoundRegionScope<'tcx>>, - kind: Kind<'tcx>, + kind: GenericArg<'tcx>, } #[derive(Clone, Debug, Default)] @@ -193,7 +194,7 @@ let placeholder = ty::PlaceholderRegion { universe, name: br }; delegate.next_placeholder_region(placeholder) } else { - delegate.next_existential_region_var() + delegate.next_existential_region_var(true) } } }; @@ -274,7 +275,7 @@ use crate::traits::WhereClause; use syntax_pos::DUMMY_SP; - match value_ty.sty { + match value_ty.kind { ty::Projection(other_projection_ty) => { let var = self.infcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, @@ -324,11 +325,11 @@ let vid = pair.vid(); let value_ty = pair.value_ty(); - // FIXME -- this logic assumes invariance, but that is wrong. + // FIXME(invariance) -- this logic assumes invariance, but that is wrong. // This only presently applies to chalk integration, as NLL // doesn't permit type variables to appear on both sides (and // doesn't use lazy norm). - match value_ty.sty { + match value_ty.kind { ty::Infer(ty::TyVar(value_vid)) => { // Two type variables: just equate them. self.infcx @@ -548,7 +549,7 @@ b = self.infcx.shallow_resolve(b); } - match (&a.sty, &b.sty) { + match (&a.kind, &b.kind) { (_, &ty::Infer(ty::TyVar(vid))) => { if D::forbid_inference_vars() { // Forbid inference variables in the RHS. @@ -616,15 +617,21 @@ fn consts( &mut self, a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, + mut b: &'tcx ty::Const<'tcx>, ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { - if let ty::Const { val: ConstValue::Infer(InferConst::Canonical(_, _)), .. } = a { - // FIXME(const_generics): I'm unsure how this branch should actually be handled, - // so this is probably not correct. - self.infcx.super_combine_consts(self, a, b) - } else { - debug!("consts(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance); - relate::super_relate_consts(self, a, b) + let a = self.infcx.shallow_resolve(a); + + if !D::forbid_inference_vars() { + b = self.infcx.shallow_resolve(b); + } + + match b.val { + ConstValue::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { + // Forbid inference variables in the RHS. + bug!("unexpected inference var {:?}", b) + } + // FIXME(invariance): see the related FIXME above. + _ => self.infcx.super_combine_consts(self, a, b) } } @@ -878,7 +885,7 @@ debug!("TypeGeneralizer::tys(a={:?})", a); - match a.sty { + match a.kind { ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) if D::forbid_inference_vars() => { @@ -991,15 +998,28 @@ a: &'tcx ty::Const<'tcx>, _: &'tcx ty::Const<'tcx>, ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { - debug!("TypeGeneralizer::consts(a={:?})", a); - - if let ty::Const { val: ConstValue::Infer(InferConst::Canonical(_, _)), .. } = a { - bug!( - "unexpected inference variable encountered in NLL generalization: {:?}", - a - ); - } else { - relate::super_relate_consts(self, a, a) + match a.val { + ConstValue::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { + bug!( + "unexpected inference variable encountered in NLL generalization: {:?}", + a + ); + } + ConstValue::Infer(InferConst::Var(vid)) => { + let mut variable_table = self.infcx.const_unification_table.borrow_mut(); + let var_value = variable_table.probe_value(vid); + match var_value.val.known() { + Some(u) => self.relate(&u, &u), + None => { + let new_var_id = variable_table.new_key(ConstVarValue { + origin: var_value.origin, + val: ConstVariableValue::Unknown { universe: self.universe }, + }); + Ok(self.tcx().mk_const_var(new_var_id, a.ty)) + } + } + } + _ => relate::super_relate_consts(self, a, a), } }
diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index c9fd339..bd19a00 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs
@@ -7,7 +7,7 @@ use crate::mir::interpret::ConstValue; use crate::traits::{self, PredicateObligation}; use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; -use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind}; +use crate::ty::subst::{InternalSubsts, GenericArg, SubstsRef, GenericArgKind}; use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use crate::util::nodemap::DefIdMap; use errors::DiagnosticBuilder; @@ -561,16 +561,14 @@ def_id, instantiated_ty ); - let gcx = self.tcx.global_tcx(); - // Use substs to build up a reverse map from regions to their // identity mappings. This is necessary because of `impl // Trait` lifetimes are computed by replacing existing // lifetimes with 'static and remapping only those used in the // `impl Trait` return type, resulting in the parameters // shifting. - let id_substs = InternalSubsts::identity_for_item(gcx, def_id); - let map: FxHashMap<Kind<'tcx>, Kind<'tcx>> = opaque_defn + let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id); + let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> = opaque_defn .substs .iter() .enumerate() @@ -720,27 +718,27 @@ return false; // keep visiting } - match ty.sty { + match ty.kind { ty::Closure(def_id, ref substs) => { // Skip lifetime parameters of the enclosing item(s) - for upvar_ty in substs.upvar_tys(def_id, self.tcx) { + for upvar_ty in substs.as_closure().upvar_tys(def_id, self.tcx) { upvar_ty.visit_with(self); } - substs.closure_sig_ty(def_id, self.tcx).visit_with(self); + substs.as_closure().sig_ty(def_id, self.tcx).visit_with(self); } ty::Generator(def_id, ref substs, _) => { // Skip lifetime parameters of the enclosing item(s) // Also skip the witness type, because that has no free regions. - for upvar_ty in substs.upvar_tys(def_id, self.tcx) { + for upvar_ty in substs.as_generator().upvar_tys(def_id, self.tcx) { upvar_ty.visit_with(self); } - substs.return_ty(def_id, self.tcx).visit_with(self); - substs.yield_ty(def_id, self.tcx).visit_with(self); + substs.as_generator().return_ty(def_id, self.tcx).visit_with(self); + substs.as_generator().yield_ty(def_id, self.tcx).visit_with(self); } _ => { ty.super_visit_with(self); @@ -759,7 +757,7 @@ tainted_by_errors: bool, opaque_type_def_id: DefId, - map: FxHashMap<Kind<'tcx>, Kind<'tcx>>, + map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, map_missing_regions_to_empty: bool, /// initially `Some`, set to `None` once error has been reported @@ -774,7 +772,7 @@ tcx: TyCtxt<'tcx>, tainted_by_errors: bool, opaque_type_def_id: DefId, - map: FxHashMap<Kind<'tcx>, Kind<'tcx>>, + map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, hidden_ty: Ty<'tcx>, span: Span, ) -> Self { @@ -789,7 +787,10 @@ } } - fn fold_kind_mapping_missing_regions_to_empty(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> { + fn fold_kind_mapping_missing_regions_to_empty( + &mut self, + kind: GenericArg<'tcx>, + ) -> GenericArg<'tcx> { assert!(!self.map_missing_regions_to_empty); self.map_missing_regions_to_empty = true; let kind = kind.fold_with(self); @@ -797,7 +798,7 @@ kind } - fn fold_kind_normally(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> { + fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { assert!(!self.map_missing_regions_to_empty); kind.fold_with(self) } @@ -822,7 +823,7 @@ let generics = self.tcx().generics_of(self.opaque_type_def_id); match self.map.get(&r.into()).map(|k| k.unpack()) { - Some(UnpackedKind::Lifetime(r1)) => r1, + Some(GenericArgKind::Lifetime(r1)) => r1, Some(u) => panic!("region mapped to unexpected kind: {:?}", u), None if generics.parent.is_some() => { if !self.map_missing_regions_to_empty && !self.tainted_by_errors { @@ -851,13 +852,13 @@ ) .emit(); - self.tcx().global_tcx().mk_region(ty::ReStatic) + self.tcx().mk_region(ty::ReStatic) }, } } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.sty { + match ty.kind { ty::Closure(def_id, substs) => { // I am a horrible monster and I pray for death. When // we encounter a closure here, it is always a closure @@ -885,7 +886,7 @@ let generics = self.tcx.generics_of(def_id); let substs = - self.tcx.mk_substs(substs.substs.iter().enumerate().map(|(index, &kind)| { + self.tcx.mk_substs(substs.iter().enumerate().map(|(index, &kind)| { if index < generics.parent_count { // Accommodate missing regions in the parent kinds... self.fold_kind_mapping_missing_regions_to_empty(kind) @@ -895,13 +896,13 @@ } })); - self.tcx.mk_closure(def_id, ty::ClosureSubsts { substs }) + self.tcx.mk_closure(def_id, substs) } ty::Generator(def_id, substs, movability) => { let generics = self.tcx.generics_of(def_id); let substs = - self.tcx.mk_substs(substs.substs.iter().enumerate().map(|(index, &kind)| { + self.tcx.mk_substs(substs.iter().enumerate().map(|(index, &kind)| { if index < generics.parent_count { // Accommodate missing regions in the parent kinds... self.fold_kind_mapping_missing_regions_to_empty(kind) @@ -911,7 +912,7 @@ } })); - self.tcx.mk_generator(def_id, ty::GeneratorSubsts { substs }, movability) + self.tcx.mk_generator(def_id, substs, movability) } ty::Param(..) => { @@ -919,7 +920,7 @@ match self.map.get(&ty.into()).map(|k| k.unpack()) { // Found it in the substitution list; replace with the parameter from the // opaque type. - Some(UnpackedKind::Type(t1)) => t1, + Some(GenericArgKind::Type(t1)) => t1, Some(u) => panic!("type mapped to unexpected kind: {:?}", u), None => { self.tcx.sess @@ -949,7 +950,7 @@ match self.map.get(&ct.into()).map(|k| k.unpack()) { // Found it in the substitution list, replace with the parameter from the // opaque type. - Some(UnpackedKind::Const(c1)) => c1, + Some(GenericArgKind::Const(c1)) => c1, Some(u) => panic!("const mapped to unexpected kind: {:?}", u), None => { self.tcx.sess @@ -988,7 +989,9 @@ value.fold_with(&mut BottomUpFolder { tcx, ty_op: |ty| { - if let ty::Opaque(def_id, substs) = ty.sty { + if ty.references_error() { + return tcx.types.err; + } else if let ty::Opaque(def_id, substs) = ty.kind { // Check that this is `impl Trait` type is // declared by `parent_def_id` -- i.e., one whose // value we are inferring. At present, this is @@ -1031,7 +1034,7 @@ .local_def_id(opaque_parent_hir_id) }; let (in_definition_scope, origin) = match tcx.hir().find(opaque_hir_id) { - Some(Node::Item(item)) => match item.node { + Some(Node::Item(item)) => match item.kind { // Anonymous `impl Trait` hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn: Some(parent), @@ -1055,7 +1058,7 @@ (def_scope_default(), hir::OpaqueTyOrigin::TypeAlias) } }, - Some(Node::ImplItem(item)) => match item.node { + Some(Node::ImplItem(item)) => match item.kind { hir::ImplItemKind::OpaqueTy(_) => ( may_define_opaque_type( tcx, @@ -1155,6 +1158,15 @@ ); debug!("instantiate_opaque_types: ty_var={:?}", ty_var); + for predicate in &bounds.predicates { + if let ty::Predicate::Projection(projection) = &predicate { + if projection.skip_binder().ty.references_error() { + // No point on adding these obligations since there's a type error involved. + return ty_var; + } + } + } + self.obligations.reserve(bounds.predicates.len()); for predicate in bounds.predicates { // Change the predicate to refer to the type variable, @@ -1201,7 +1213,7 @@ let mut hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); // Named opaque types can be defined by any siblings or children of siblings. - let scope = tcx.hir().get_defining_scope(opaque_hir_id).expect("could not get defining scope"); + let scope = tcx.hir().get_defining_scope(opaque_hir_id); // We walk up the node tree until we hit the root or the scope of the opaque type. while hir_id != scope && hir_id != hir::CRATE_HIR_ID { hir_id = tcx.hir().get_parent_item(hir_id);
diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index e1470e4..f780618 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs
@@ -67,7 +67,7 @@ use crate::traits::ObligationCause; use crate::ty::outlives::Component; use crate::ty::{self, Region, Ty, TyCtxt, TypeFoldable}; -use crate::ty::subst::UnpackedKind; +use crate::ty::subst::GenericArgKind; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// Registers that the given region obligation must be resolved @@ -403,7 +403,7 @@ // 'a` in the environment but `trait Foo<'b> { type Item: 'b // }` in the trait definition. approx_env_bounds.retain(|bound| { - match bound.0.sty { + match bound.0.kind { ty::Projection(projection_ty) => { self.verify_bound.projection_declared_bounds_from_trait(projection_ty) .all(|r| r != bound.1) @@ -433,13 +433,13 @@ for k in projection_ty.substs { match k.unpack() { - UnpackedKind::Lifetime(lt) => { + GenericArgKind::Lifetime(lt) => { self.delegate.push_sub_region_constraint(origin.clone(), region, lt); } - UnpackedKind::Type(ty) => { + GenericArgKind::Type(ty) => { self.type_must_outlive(origin.clone(), ty, region); } - UnpackedKind::Const(_) => { + GenericArgKind::Const(_) => { // Const parameters don't impose constraints. } }
diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs index f23e52f..3110b02 100644 --- a/src/librustc/infer/outlives/verify.rs +++ b/src/librustc/infer/outlives/verify.rs
@@ -44,7 +44,7 @@ } fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - match ty.sty { + match ty.kind { ty::Param(p) => self.param_bound(p), ty::Projection(data) => self.projection_bound(data), _ => self.recursive_type_bound(ty), @@ -87,7 +87,7 @@ let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx); let erased_projection_ty = self.tcx.erase_regions(&projection_ty); self.declared_generic_bounds_from_env_with_compare_fn(|ty| { - if let ty::Projection(..) = ty.sty { + if let ty::Projection(..) = ty.kind { let erased_ty = self.tcx.erase_regions(&ty); erased_ty == erased_projection_ty } else {
diff --git a/src/librustc/infer/region_constraints/leak_check.rs b/src/librustc/infer/region_constraints/leak_check.rs index 0c83bbc..3d06942 100644 --- a/src/librustc/infer/region_constraints/leak_check.rs +++ b/src/librustc/infer/region_constraints/leak_check.rs
@@ -14,9 +14,11 @@ /// retain the older (arguably incorrect) behavior of the /// compiler. /// - /// NB. The use of snapshot here is mostly an efficiency thing -- - /// we could search *all* region constraints, but that'd be a - /// bigger set and the data structures are not setup for that. If + /// NB. Although `_snapshot` isn't used, it's passed in to prove + /// that we are in a snapshot, which guarantees that we can just + /// search the "undo log" for edges. This is mostly an efficiency + /// thing -- we could search *all* region constraints, but that'd be + /// a bigger set and the data structures are not setup for that. If /// we wind up keeping some form of this check long term, it would /// probably be better to remove the snapshot parameter and to /// refactor the constraint set.
diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 21904ed..8c6a7c9 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs
@@ -7,7 +7,7 @@ use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc_index::vec::IndexVec; use rustc_data_structures::sync::Lrc; use rustc_data_structures::unify as ut; use crate::hir::def_id::DefId; @@ -116,7 +116,7 @@ } /// Represents a constraint that influences the inference process. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] pub enum Constraint<'tcx> { /// A region variable is a subregion of another. VarSubVar(RegionVid, RegionVid),
diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index 7e553d7..7c3a338 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs
@@ -1,7 +1,7 @@ use super::{InferCtxt, FixupError, FixupResult, Span}; use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::mir::interpret::ConstValue; -use crate::ty::{self, Ty, Const, TyCtxt, TypeFoldable, InferConst, TypeFlags}; +use crate::ty::{self, Ty, Const, TyCtxt, TypeFoldable, InferConst}; use crate::ty::fold::{TypeFolder, TypeVisitor}; /////////////////////////////////////////////////////////////////////////// @@ -29,7 +29,7 @@ } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.has_infer_types() { + if !t.has_infer_types() && !t.has_infer_consts() { t // micro-optimize -- if there is nothing in this type that this fold affects... } else { let t = self.infcx.shallow_resolve(t); @@ -38,7 +38,7 @@ } fn fold_const(&mut self, ct: &'tcx Const<'tcx>) -> &'tcx Const<'tcx> { - if !ct.has_type_flags(TypeFlags::HAS_CT_INFER) { + if !ct.has_infer_consts() { ct // micro-optimize -- if there is nothing in this const that this fold affects... } else { let ct = self.infcx.shallow_resolve(ct); @@ -118,7 +118,7 @@ fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { let t = self.infcx.shallow_resolve(t); if t.has_infer_types() { - if let ty::Infer(infer_ty) = t.sty { + if let ty::Infer(infer_ty) = t.kind { // Since we called `shallow_resolve` above, this must // be an (as yet...) unresolved inference variable. let ty_var_span = @@ -188,7 +188,7 @@ // defaulted tuples. } else { let t = self.infcx.shallow_resolve(t); - match t.sty { + match t.kind { ty::Infer(ty::TyVar(vid)) => { self.err = Some(FixupError::UnresolvedTy(vid)); self.tcx().types.err
diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index 67c97ef5..21c847e 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs
@@ -71,7 +71,7 @@ let infcx = self.fields.infcx; let a = infcx.type_variables.borrow_mut().replace_if_possible(a); let b = infcx.type_variables.borrow_mut().replace_if_possible(b); - match (&a.sty, &b.sty) { + match (&a.kind, &b.kind) { (&ty::Infer(TyVar(a_vid)), &ty::Infer(TyVar(b_vid))) => { // Shouldn't have any LBR here, so we can safely put // this under a binder below without fear of accidental
diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs index e30e869..f79a30c 100644 --- a/src/librustc/infer/type_variable.rs +++ b/src/librustc/infer/type_variable.rs
@@ -1,4 +1,4 @@ -use syntax::symbol::InternedString; +use syntax::symbol::Symbol; use syntax_pos::Span; use crate::ty::{self, Ty, TyVid}; @@ -49,7 +49,7 @@ MiscVariable, NormalizeProjectionType, TypeInference, - TypeParameterDefinition(InternedString), + TypeParameterDefinition(Symbol), /// One of the upvars or closure kind parameters in a `ClosureSubsts` /// (before it has been determined). @@ -234,14 +234,20 @@ /// Retrieves the type to which `vid` has been instantiated, if /// any. pub fn probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { - self.eq_relations.probe_value(vid) + self.inlined_probe(vid) + } + + /// An always-inlined variant of `probe`, for very hot call sites. + #[inline(always)] + pub fn inlined_probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { + self.eq_relations.inlined_probe_value(vid) } /// If `t` is a type-inference variable, and it has been /// instantiated, then return the with which it was /// instantiated. Otherwise, returns `t`. pub fn replace_if_possible(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.sty { + match t.kind { ty::Infer(ty::TyVar(v)) => { match self.probe(v) { TypeVariableValue::Unknown { .. } => t,
diff --git a/src/librustc/infer/unify_key.rs b/src/librustc/infer/unify_key.rs index 846611d..b0b6d97 100644 --- a/src/librustc/infer/unify_key.rs +++ b/src/librustc/infer/unify_key.rs
@@ -3,7 +3,7 @@ use rustc_data_structures::unify::{NoError, EqUnifyValue, UnifyKey, UnifyValue, UnificationTable}; use rustc_data_structures::unify::InPlace; use syntax_pos::{Span, DUMMY_SP}; -use syntax::symbol::InternedString; +use syntax::symbol::Symbol; use std::cmp; use std::marker::PhantomData; @@ -90,7 +90,7 @@ pub enum ConstVariableOriginKind { MiscVariable, ConstInference, - ConstParameterDefinition(InternedString), + ConstParameterDefinition(Symbol), SubstitutionPlaceholder, }
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 7a01ae6..996f5b1 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs
@@ -35,15 +35,15 @@ #![feature(const_transmute)] #![feature(core_intrinsics)] #![feature(drain_filter)] -#![feature(inner_deref)] #![cfg_attr(windows, feature(libc))] #![feature(never_type)] #![feature(exhaustive_patterns)] #![feature(overlapping_marker_traits)] #![feature(extern_types)] #![feature(nll)] -#![feature(non_exhaustive)] +#![cfg_attr(bootstrap, feature(non_exhaustive))] #![feature(optin_builtin_traits)] +#![feature(option_expect_none)] #![feature(range_is_empty)] #![feature(slice_patterns)] #![feature(specialization)] @@ -57,9 +57,8 @@ #![feature(test)] #![feature(in_band_lifetimes)] #![feature(crate_visibility_modifier)] -#![feature(proc_macro_hygiene)] +#![cfg_attr(bootstrap, feature(proc_macro_hygiene))] #![feature(log_syntax)] -#![feature(mem_take)] #![feature(associated_type_bounds)] #![feature(rustc_attrs)] @@ -100,19 +99,14 @@ pub mod lint; pub mod middle { - pub mod borrowck; pub mod expr_use_visitor; pub mod cstore; - pub mod dead; pub mod dependency_format; pub mod diagnostic_items; - pub mod entry; pub mod exported_symbols; pub mod free_region; - pub mod intrinsicck; pub mod lib_features; pub mod lang_items; - pub mod liveness; pub mod mem_categorization; pub mod privacy; pub mod reachable;
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 5906a63..5c871bb 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs
@@ -4,11 +4,12 @@ //! compiler code, rather than using their own custom pass. Those //! lints are all available in `rustc_lint::builtin`. -use crate::lint::{LintPass, LateLintPass, LintArray}; +use crate::lint::{LintPass, LateLintPass, LintArray, FutureIncompatibleInfo}; use crate::middle::stability; use crate::session::Session; use errors::{Applicability, DiagnosticBuilder, pluralise}; use syntax::ast; +use syntax::edition::Edition; use syntax::source_map::Span; use syntax::symbol::Symbol; @@ -21,7 +22,8 @@ declare_lint! { pub CONST_ERR, Deny, - "constant evaluation detected erroneous expression" + "constant evaluation detected erroneous expression", + report_in_external_macro } declare_lint! { @@ -67,10 +69,16 @@ } declare_lint! { + pub UNUSED_ATTRIBUTES, + Warn, + "detects attributes that were not used by the compiler" +} + +declare_lint! { pub UNREACHABLE_CODE, Warn, "detects unreachable code paths", - report_in_external_macro: true + report_in_external_macro } declare_lint! { @@ -80,6 +88,12 @@ } declare_lint! { + pub OVERLAPPING_PATTERNS, + Warn, + "detects overlapping patterns" +} + +declare_lint! { pub UNUSED_MACROS, Warn, "detects macros that were not used" @@ -124,7 +138,11 @@ declare_lint! { pub PRIVATE_IN_PUBLIC, Warn, - "detect private items in public interfaces not caught by the old implementation" + "detect private items in public interfaces not caught by the old implementation", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>", + edition: None, + }; } declare_lint! { @@ -136,13 +154,21 @@ declare_lint! { pub PUB_USE_OF_PRIVATE_EXTERN_CRATE, Deny, - "detect public re-exports of private extern crates" + "detect public re-exports of private extern crates", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>", + edition: None, + }; } declare_lint! { pub INVALID_TYPE_PARAM_DEFAULT, Deny, - "type parameter default erroneously allowed in invalid location" + "type parameter default erroneously allowed in invalid location", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #36887 <https://github.com/rust-lang/rust/issues/36887>", + edition: None, + }; } declare_lint! { @@ -154,63 +180,99 @@ declare_lint! { pub SAFE_EXTERN_STATICS, Deny, - "safe access to extern statics was erroneously allowed" + "safe access to extern statics was erroneously allowed", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #36247 <https://github.com/rust-lang/rust/issues/36247>", + edition: None, + }; } declare_lint! { pub SAFE_PACKED_BORROWS, Warn, - "safe borrows of fields of packed structs were was erroneously allowed" + "safe borrows of fields of packed structs were was erroneously allowed", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #46043 <https://github.com/rust-lang/rust/issues/46043>", + edition: None, + }; } declare_lint! { pub PATTERNS_IN_FNS_WITHOUT_BODY, Warn, - "patterns in functions without body were erroneously allowed" + "patterns in functions without body were erroneously allowed", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #35203 <https://github.com/rust-lang/rust/issues/35203>", + edition: None, + }; } declare_lint! { pub LEGACY_DIRECTORY_OWNERSHIP, Deny, "non-inline, non-`#[path]` modules (e.g., `mod foo;`) were erroneously allowed in some files \ - not named `mod.rs`" + not named `mod.rs`", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #37872 <https://github.com/rust-lang/rust/issues/37872>", + edition: None, + }; } declare_lint! { pub LEGACY_CONSTRUCTOR_VISIBILITY, Deny, - "detects use of struct constructors that would be invisible with new visibility rules" + "detects use of struct constructors that would be invisible with new visibility rules", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #39207 <https://github.com/rust-lang/rust/issues/39207>", + edition: None, + }; } declare_lint! { pub MISSING_FRAGMENT_SPECIFIER, Deny, - "detects missing fragment specifiers in unused `macro_rules!` patterns" + "detects missing fragment specifiers in unused `macro_rules!` patterns", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #40107 <https://github.com/rust-lang/rust/issues/40107>", + edition: None, + }; } declare_lint! { pub PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES, Deny, - "detects parenthesized generic parameters in type and module names" + "detects parenthesized generic parameters in type and module names", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #42238 <https://github.com/rust-lang/rust/issues/42238>", + edition: None, + }; } declare_lint! { pub LATE_BOUND_LIFETIME_ARGUMENTS, Warn, - "detects generic lifetime arguments in path segments with late bound lifetime parameters" + "detects generic lifetime arguments in path segments with late bound lifetime parameters", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #42868 <https://github.com/rust-lang/rust/issues/42868>", + edition: None, + }; } declare_lint! { pub ORDER_DEPENDENT_TRAIT_OBJECTS, Deny, - "trait-object types were treated as different depending on marker-trait order" + "trait-object types were treated as different depending on marker-trait order", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #56484 <https://github.com/rust-lang/rust/issues/56484>", + edition: None, + }; } declare_lint! { pub DEPRECATED, Warn, "detects use of deprecated items", - report_in_external_macro: true + report_in_external_macro } declare_lint! { @@ -246,7 +308,11 @@ declare_lint! { pub TYVAR_BEHIND_RAW_POINTER, Warn, - "raw pointer to an inference variable" + "raw pointer to an inference variable", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #46906 <https://github.com/rust-lang/rust/issues/46906>", + edition: Some(Edition::Edition2018), + }; } declare_lint! { @@ -265,19 +331,33 @@ pub ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, Allow, "fully qualified paths that start with a module name \ - instead of `crate`, `self`, or an extern crate name" + instead of `crate`, `self`, or an extern crate name", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #53130 <https://github.com/rust-lang/rust/issues/53130>", + edition: Some(Edition::Edition2018), + }; } declare_lint! { pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, Warn, - "floating-point literals cannot be used in patterns" + "floating-point literals cannot be used in patterns", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #41620 <https://github.com/rust-lang/rust/issues/41620>", + edition: None, + }; } declare_lint! { pub UNSTABLE_NAME_COLLISIONS, Warn, - "detects name collision with an existing but unstable method" + "detects name collision with an existing but unstable method", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #48919 <https://github.com/rust-lang/rust/issues/48919>", + edition: None, + // Note: this item represents future incompatibility of all unstable functions in the + // standard library, and thus should never be removed or changed to an error. + }; } declare_lint! { @@ -295,7 +375,11 @@ declare_lint! { pub DUPLICATE_MACRO_EXPORTS, Deny, - "detects duplicate macro exports" + "detects duplicate macro exports", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #35896 <https://github.com/rust-lang/rust/issues/35896>", + edition: Some(Edition::Edition2018), + }; } declare_lint! { @@ -319,13 +403,21 @@ declare_lint! { pub WHERE_CLAUSES_OBJECT_SAFETY, Warn, - "checks the object safety of where clauses" + "checks the object safety of where clauses", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #51443 <https://github.com/rust-lang/rust/issues/51443>", + edition: None, + }; } declare_lint! { pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, Warn, - "detects proc macro derives using inaccessible names from parent modules" + "detects proc macro derives using inaccessible names from parent modules", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #50504 <https://github.com/rust-lang/rust/issues/50504>", + edition: None, + }; } declare_lint! { @@ -339,7 +431,11 @@ pub MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, Deny, "macro-expanded `macro_export` macros from the current crate \ - cannot be referred to by absolute paths" + cannot be referred to by absolute paths", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #52234 <https://github.com/rust-lang/rust/issues/52234>", + edition: None, + }; } declare_lint! { @@ -352,7 +448,11 @@ pub INDIRECT_STRUCTURAL_MATCH, // defaulting to allow until rust-lang/rust#62614 is fixed. Allow, - "pattern with const indirectly referencing non-`#[structural_match]` type" + "pattern with const indirectly referencing non-`#[structural_match]` type", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #62411 <https://github.com/rust-lang/rust/issues/62411>", + edition: None, + }; } /// Some lints that are buffered from `libsyntax`. See `syntax::early_buffered_lints`. @@ -360,7 +460,11 @@ declare_lint! { pub ILL_FORMED_ATTRIBUTE_INPUT, Warn, - "ill-formed attribute inputs that were previously accepted and used in practice" + "ill-formed attribute inputs that were previously accepted and used in practice", + @future_incompatible = super::FutureIncompatibleInfo { + reference: "issue #57571 <https://github.com/rust-lang/rust/issues/57571>", + edition: None, + }; } declare_lint! { @@ -368,37 +472,59 @@ Allow, "possible meta-variable misuse at macro definition" } + + declare_lint! { + pub INCOMPLETE_INCLUDE, + Deny, + "trailing content in included file" + } } declare_lint! { pub DEPRECATED_IN_FUTURE, Allow, "detects use of items that will be deprecated in a future version", - report_in_external_macro: true + report_in_external_macro } declare_lint! { pub AMBIGUOUS_ASSOCIATED_ITEMS, Deny, - "ambiguous associated items" + "ambiguous associated items", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #57644 <https://github.com/rust-lang/rust/issues/57644>", + edition: None, + }; } declare_lint! { pub NESTED_IMPL_TRAIT, Warn, - "nested occurrence of `impl Trait` type" + "nested occurrence of `impl Trait` type", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #59014 <https://github.com/rust-lang/rust/issues/59014>", + edition: None, + }; } declare_lint! { pub MUTABLE_BORROW_RESERVATION_CONFLICT, Warn, - "reservation of a two-phased borrow conflicts with other shared borrows" + "reservation of a two-phased borrow conflicts with other shared borrows", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>", + edition: None, + }; } declare_lint! { pub SOFT_UNSTABLE, Deny, - "a feature gate that doesn't break dependent crates" + "a feature gate that doesn't break dependent crates", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #64266 <https://github.com/rust-lang/rust/issues/64266>", + edition: None, + }; } declare_lint_pass! { @@ -416,6 +542,7 @@ DEAD_CODE, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, + OVERLAPPING_PATTERNS, UNUSED_MACROS, WARNINGS, UNUSED_FEATURES,
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index c658120..eef1cee 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs
@@ -22,23 +22,21 @@ use crate::hir::intravisit::Visitor; use crate::hir::map::{definitions::DisambiguatedDefPathData, DefPathData}; use crate::lint::{EarlyLintPass, LateLintPass, EarlyLintPassObject, LateLintPassObject}; -use crate::lint::{LintArray, Level, Lint, LintId, LintPass, LintBuffer}; +use crate::lint::{Level, Lint, LintId, LintPass, LintBuffer, FutureIncompatibleInfo}; use crate::lint::builtin::BuiltinLintDiagnostics; use crate::lint::levels::{LintLevelSets, LintLevelsBuilder}; use crate::middle::privacy::AccessLevels; -use crate::session::{config, early_error, Session}; -use crate::ty::{self, print::Printer, subst::Kind, TyCtxt, Ty}; +use crate::session::Session; +use crate::ty::{self, print::Printer, subst::GenericArg, TyCtxt, Ty}; use crate::ty::layout::{LayoutError, LayoutOf, TyLayout}; use crate::util::nodemap::FxHashMap; use crate::util::common::time; use errors::DiagnosticBuilder; use std::slice; -use std::default::Default as StdDefault; -use rustc_data_structures::sync::{ReadGuard, Lock, ParallelIterator, join, par_iter}; +use rustc_data_structures::sync::{self, ParallelIterator, join, par_iter}; use rustc_serialize::{Decoder, Decodable, Encoder, Encodable}; use syntax::ast; -use syntax::edition; use syntax::util::lev_distance::find_best_match_for_name; use syntax::visit as ast_visit; use syntax_pos::{MultiSpan, Span, symbol::Symbol}; @@ -50,24 +48,25 @@ pub struct LintStore { /// Registered lints. The bool is true if the lint was /// added by a plugin. - lints: Vec<(&'static Lint, bool)>, + lints: Vec<&'static Lint>, - /// Trait objects for each lint pass. - /// This is only `None` while performing a lint pass. - pre_expansion_passes: Option<Vec<EarlyLintPassObject>>, - early_passes: Option<Vec<EarlyLintPassObject>>, - late_passes: Lock<Option<Vec<LateLintPassObject>>>, - late_module_passes: Vec<LateLintPassObject>, + /// Constructor functions for each variety of lint pass. + /// + /// These should only be called once, but since we want to avoid locks or + /// interior mutability, we don't enforce this (and lints should, in theory, + /// be compatible with being constructed more than once, though not + /// necessarily in a sane manner. This is safe though.) + pre_expansion_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>, + early_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>, + late_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>, + /// This is unique in that we construct them per-module, so not once. + late_module_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>, /// Lints indexed by name. by_name: FxHashMap<String, TargetLint>, /// Map of registered lint groups to what lints they expand to. lint_groups: FxHashMap<&'static str, LintGroup>, - - /// Extra info for future incompatibility lints, describing the - /// issue or RFC that caused the incompatibility. - future_incompatible: FxHashMap<LintId, FutureIncompatibleInfo>, } /// Lints that are buffered up early on in the `Session` before the @@ -81,18 +80,6 @@ pub diagnostic: BuiltinLintDiagnostics, } -/// Extra information for a future incompatibility lint. See the call -/// to `register_future_incompatible` in `librustc_lint/lib.rs` for -/// guidelines. -pub struct FutureIncompatibleInfo { - pub id: LintId, - /// e.g., a URL for an issue/PR/RFC or error code - pub reference: &'static str, - /// If this is an edition fixing lint, the edition in which - /// this lint becomes obsolete - pub edition: Option<edition::Edition>, -} - /// The target of the `by_name` map, which accounts for renaming/deprecation. enum TargetLint { /// A direct lint target @@ -142,17 +129,16 @@ pub fn new() -> LintStore { LintStore { lints: vec![], - pre_expansion_passes: Some(vec![]), - early_passes: Some(vec![]), - late_passes: Lock::new(Some(vec![])), + pre_expansion_passes: vec![], + early_passes: vec![], + late_passes: vec![], late_module_passes: vec![], by_name: Default::default(), - future_incompatible: Default::default(), lint_groups: Default::default(), } } - pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] { + pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] { &self.lints } @@ -168,101 +154,66 @@ .collect() } - pub fn register_early_pass(&mut self, - sess: Option<&Session>, - from_plugin: bool, - register_only: bool, - pass: EarlyLintPassObject) { - self.push_pass(sess, from_plugin, &pass); - if !register_only { - self.early_passes.as_mut().unwrap().push(pass); - } + pub fn register_early_pass( + &mut self, + pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync + ) { + self.early_passes.push(Box::new(pass)); } pub fn register_pre_expansion_pass( &mut self, - sess: Option<&Session>, - from_plugin: bool, - register_only: bool, - pass: EarlyLintPassObject, + pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, ) { - self.push_pass(sess, from_plugin, &pass); - if !register_only { - self.pre_expansion_passes.as_mut().unwrap().push(pass); - } + self.pre_expansion_passes.push(Box::new(pass)); } - pub fn register_late_pass(&mut self, - sess: Option<&Session>, - from_plugin: bool, - register_only: bool, - per_module: bool, - pass: LateLintPassObject) { - self.push_pass(sess, from_plugin, &pass); - if !register_only { - if per_module { - self.late_module_passes.push(pass); - } else { - self.late_passes.lock().as_mut().unwrap().push(pass); - } - } + pub fn register_late_pass( + &mut self, + pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync, + ) { + self.late_passes.push(Box::new(pass)); + } + + pub fn register_late_mod_pass( + &mut self, + pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync, + ) { + self.late_module_passes.push(Box::new(pass)); } // Helper method for register_early/late_pass - fn push_pass<P: LintPass + ?Sized + 'static>(&mut self, - sess: Option<&Session>, - from_plugin: bool, - pass: &Box<P>) { - for lint in pass.get_lints() { - self.lints.push((lint, from_plugin)); + pub fn register_lints(&mut self, lints: &[&'static Lint]) { + for lint in lints { + self.lints.push(lint); let id = LintId::of(lint); if self.by_name.insert(lint.name_lower(), Id(id)).is_some() { - let msg = format!("duplicate specification of lint {}", lint.name_lower()); - match (sess, from_plugin) { - // We load builtin lints first, so a duplicate is a compiler bug. - // Use early_error when handling -W help with no crate. - (None, _) => early_error(config::ErrorOutputType::default(), &msg[..]), - (Some(_), false) => bug!("{}", msg), + bug!("duplicate specification of lint {}", lint.name_lower()) + } - // A duplicate name from a plugin is a user error. - (Some(sess), true) => sess.err(&msg[..]), + if let Some(FutureIncompatibleInfo { edition, .. }) = lint.future_incompatible { + if let Some(edition) = edition { + self.lint_groups.entry(edition.lint_name()) + .or_insert(LintGroup { + lint_ids: vec![], + from_plugin: lint.is_plugin, + depr: None, + }) + .lint_ids.push(id); } + + self.lint_groups.entry("future_incompatible") + .or_insert(LintGroup { + lint_ids: vec![], + from_plugin: lint.is_plugin, + depr: None, + }) + .lint_ids.push(id); } } } - pub fn register_future_incompatible(&mut self, - sess: Option<&Session>, - lints: Vec<FutureIncompatibleInfo>) { - - for edition in edition::ALL_EDITIONS { - let lints = lints.iter().filter(|f| f.edition == Some(*edition)).map(|f| f.id) - .collect::<Vec<_>>(); - if !lints.is_empty() { - self.register_group(sess, false, edition.lint_name(), None, lints) - } - } - - let mut future_incompatible = Vec::with_capacity(lints.len()); - for lint in lints { - future_incompatible.push(lint.id); - self.future_incompatible.insert(lint.id, lint); - } - - self.register_group( - sess, - false, - "future_incompatible", - None, - future_incompatible, - ); - } - - pub fn future_incompatible(&self, id: LintId) -> Option<&FutureIncompatibleInfo> { - self.future_incompatible.get(&id) - } - pub fn register_group_alias( &mut self, lint_name: &'static str, @@ -277,7 +228,6 @@ pub fn register_group( &mut self, - sess: Option<&Session>, from_plugin: bool, name: &'static str, deprecated_name: Option<&'static str>, @@ -300,16 +250,7 @@ } if !new { - let msg = format!("duplicate specification of lint group {}", name); - match (sess, from_plugin) { - // We load builtin lints first, so a duplicate is a compiler bug. - // Use early_error when handling -W help with no crate. - (None, _) => early_error(config::ErrorOutputType::default(), &msg[..]), - (Some(_), false) => bug!("{}", msg), - - // A duplicate name from a plugin is a user error. - (Some(sess), true) => sess.err(&msg[..]), - } + bug!("duplicate specification of lint group {}", name); } } @@ -522,7 +463,7 @@ pub access_levels: &'a AccessLevels, /// The store of registered lints and the lint levels. - lint_store: ReadGuard<'a, LintStore>, + lint_store: &'tcx LintStore, last_node_with_lint_attrs: hir::HirId, @@ -550,7 +491,7 @@ builder: LintLevelsBuilder<'a>, /// The store of registered lints and the lint levels. - lint_store: ReadGuard<'a, LintStore>, + lint_store: &'a LintStore, buffered: LintBuffer, } @@ -639,14 +580,16 @@ impl<'a> EarlyContext<'a> { fn new( sess: &'a Session, + lint_store: &'a LintStore, krate: &'a ast::Crate, buffered: LintBuffer, + warn_about_weird_lints: bool, ) -> EarlyContext<'a> { EarlyContext { sess, krate, - lint_store: sess.lint_store.borrow(), - builder: LintLevelSets::builder(sess), + lint_store, + builder: LintLevelSets::builder(sess, warn_about_weird_lints, lint_store), buffered, } } @@ -681,7 +624,7 @@ f: F) where F: FnOnce(&mut Self) { - let push = self.context.builder.push(attrs); + let push = self.context.builder.push(attrs, &self.context.lint_store); self.check_id(id); self.enter_attrs(attrs); f(self); @@ -829,7 +772,7 @@ trait_ref: Option<ty::TraitRef<'tcx>>, ) -> Result<Self::Path, Self::Error> { if trait_ref.is_none() { - if let ty::Adt(def, substs) = self_ty.sty { + if let ty::Adt(def, substs) = self_ty.kind { return self.print_def_path(def.did, substs); } } @@ -875,14 +818,14 @@ _ => {} } - path.push(disambiguated_data.data.as_interned_str().as_symbol()); + path.push(disambiguated_data.data.as_symbol()); Ok(path) } fn path_generic_args( self, print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, - _args: &[Kind<'tcx>], + _args: &[GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { print_prefix(self) } @@ -981,7 +924,7 @@ fn visit_item(&mut self, it: &'tcx hir::Item) { let generics = self.context.generics.take(); - self.context.generics = it.node.generics(); + self.context.generics = it.kind.generics(); self.with_lint_attrs(it.hir_id, &it.attrs, |cx| { cx.with_param_env(it.hir_id, |cx| { lint_callback!(cx, check_item, it); @@ -1355,10 +1298,6 @@ fn name(&self) -> &'static str { panic!() } - - fn get_lints(&self) -> LintArray { - panic!() - } } macro_rules! expand_late_lint_pass_impl_methods { @@ -1393,7 +1332,7 @@ tables: &ty::TypeckTables::empty(None), param_env: ty::ParamEnv::empty(), access_levels, - lint_store: tcx.sess.lint_store.borrow(), + lint_store: &tcx.lint_store, last_node_with_lint_attrs: tcx.hir().as_local_hir_id(module_def_id).unwrap(), generics: None, only_module: true, @@ -1425,8 +1364,8 @@ late_lint_mod_pass(tcx, module_def_id, builtin_lints); - let mut passes: Vec<_> = tcx.sess.lint_store.borrow().late_module_passes - .iter().map(|pass| pass.fresh_late_pass()).collect(); + let mut passes: Vec<_> = tcx.lint_store.late_module_passes + .iter().map(|pass| (pass)()).collect(); if !passes.is_empty() { late_lint_mod_pass(tcx, module_def_id, LateLintPassObjects { lints: &mut passes[..] }); @@ -1443,7 +1382,7 @@ tables: &ty::TypeckTables::empty(None), param_env: ty::ParamEnv::empty(), access_levels, - lint_store: tcx.sess.lint_store.borrow(), + lint_store: &tcx.lint_store, last_node_with_lint_attrs: hir::CRATE_HIR_ID, generics: None, only_module: false, @@ -1467,7 +1406,8 @@ } fn late_lint_crate<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(tcx: TyCtxt<'tcx>, builtin_lints: T) { - let mut passes = tcx.sess.lint_store.borrow().late_passes.lock().take().unwrap(); + let mut passes = tcx.lint_store + .late_passes.iter().map(|p| (p)()).collect::<Vec<_>>(); if !tcx.sess.opts.debugging_opts.no_interleave_lints { if !passes.is_empty() { @@ -1482,8 +1422,8 @@ }); } - let mut passes: Vec<_> = tcx.sess.lint_store.borrow().late_module_passes - .iter().map(|pass| pass.fresh_late_pass()).collect(); + let mut passes: Vec<_> = tcx.lint_store.late_module_passes + .iter().map(|pass| (pass)()).collect(); for pass in &mut passes { time(tcx.sess, &format!("running late module lint: {}", pass.name()), || { @@ -1491,9 +1431,6 @@ }); } } - - // Put the passes back in the session. - *tcx.sess.lint_store.borrow().late_passes.lock() = Some(passes); } /// Performs lint checking on a crate. @@ -1510,7 +1447,7 @@ time(tcx.sess, "module lints", || { // Run per-module lints par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| { - tcx.ensure().lint_mod(tcx.hir().local_def_id_from_node_id(module)); + tcx.ensure().lint_mod(tcx.hir().local_def_id(module)); }); }); }); @@ -1525,10 +1462,6 @@ fn name(&self) -> &'static str { panic!() } - - fn get_lints(&self) -> LintArray { - panic!() - } } macro_rules! expand_early_lint_pass_impl_methods { @@ -1553,12 +1486,14 @@ fn early_lint_crate<T: EarlyLintPass>( sess: &Session, + lint_store: &LintStore, krate: &ast::Crate, pass: T, buffered: LintBuffer, + warn_about_weird_lints: bool, ) -> LintBuffer { let mut cx = EarlyContextAndPass { - context: EarlyContext::new(sess, krate, buffered), + context: EarlyContext::new(sess, lint_store, krate, buffered, warn_about_weird_lints), pass, }; @@ -1577,31 +1512,31 @@ pub fn check_ast_crate<T: EarlyLintPass>( sess: &Session, + lint_store: &LintStore, krate: &ast::Crate, pre_expansion: bool, + lint_buffer: Option<LintBuffer>, builtin_lints: T, ) { - let (mut passes, mut buffered) = if pre_expansion { - ( - sess.lint_store.borrow_mut().pre_expansion_passes.take().unwrap(), - LintBuffer::default(), - ) + let mut passes: Vec<_> = if pre_expansion { + lint_store.pre_expansion_passes.iter().map(|p| (p)()).collect() } else { - ( - sess.lint_store.borrow_mut().early_passes.take().unwrap(), - sess.buffered_lints.borrow_mut().take().unwrap(), - ) + lint_store.early_passes.iter().map(|p| (p)()).collect() }; + let mut buffered = lint_buffer.unwrap_or_default(); if !sess.opts.debugging_opts.no_interleave_lints { - buffered = early_lint_crate(sess, krate, builtin_lints, buffered); + buffered = early_lint_crate(sess, lint_store, krate, builtin_lints, buffered, + pre_expansion); if !passes.is_empty() { buffered = early_lint_crate( sess, + lint_store, krate, EarlyLintPassObjects { lints: &mut passes[..] }, buffered, + pre_expansion, ); } } else { @@ -1609,21 +1544,16 @@ buffered = time(sess, &format!("running lint: {}", pass.name()), || { early_lint_crate( sess, + lint_store, krate, EarlyLintPassObjects { lints: slice::from_mut(pass) }, buffered, + pre_expansion, ) }); } } - // Put the lint store levels and passes back in the session. - if pre_expansion { - sess.lint_store.borrow_mut().pre_expansion_passes = Some(passes); - } else { - sess.lint_store.borrow_mut().early_passes = Some(passes); - } - // All of the buffered lints should have been emitted at this point. // If not, that means that we somehow buffered a lint for a node id // that was not lint-checked (perhaps it doesn't exist?). This is a bug. @@ -1653,7 +1583,7 @@ fn decode<D: Decoder>(d: &mut D) -> Result<LintId, D::Error> { let s = d.read_str()?; ty::tls::with(|tcx| { - match tcx.sess.lint_store.borrow().find_lints(&s) { + match tcx.lint_store.find_lints(&s) { Ok(ids) => { if ids.len() != 0 { panic!("invalid lint-id `{}`", s);
diff --git a/src/librustc/lint/internal.rs b/src/librustc/lint/internal.rs index 13834ea..a08722e 100644 --- a/src/librustc/lint/internal.rs +++ b/src/librustc/lint/internal.rs
@@ -94,7 +94,7 @@ } fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &'tcx Ty) { - match &ty.node { + match &ty.kind { TyKind::Path(qpath) => { if let QPath::Resolved(_, path) = qpath { if let Some(last) = path.segments.iter().last() { @@ -169,7 +169,7 @@ } fn is_ty_or_ty_ctxt(cx: &LateContext<'_, '_>, ty: &Ty) -> Option<String> { - match &ty.node { + match &ty.kind { TyKind::Path(qpath) => { if let QPath::Resolved(_, path) = qpath { let did = path.res.opt_def_id()?; @@ -218,7 +218,7 @@ impl EarlyLintPass for LintPassImpl { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::Impl(_, _, _, _, Some(lint_pass), _, _) = &item.node { + if let ItemKind::Impl(_, _, _, _, Some(lint_pass), _, _) = &item.kind { if let Some(last) = lint_pass.path.segments.last() { if last.ident.name == sym::LintPass { let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs index cbc6dbd..e470dbd 100644 --- a/src/librustc/lint/levels.rs +++ b/src/librustc/lint/levels.rs
@@ -3,16 +3,16 @@ use crate::hir::HirId; use crate::ich::StableHashingContext; use crate::lint::builtin; -use crate::lint::context::CheckLintNameResult; +use crate::lint::context::{LintStore, CheckLintNameResult}; use crate::lint::{self, Lint, LintId, Level, LintSource}; use crate::session::Session; use crate::util::nodemap::FxHashMap; use errors::{Applicability, DiagnosticBuilder}; -use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, - StableHasher, StableHasherResult}; +use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, StableHasher}; use syntax::ast; use syntax::attr; use syntax::feature_gate; +use syntax::print::pprust; use syntax::source_map::MultiSpan; use syntax::symbol::{Symbol, sym}; @@ -35,21 +35,24 @@ } impl LintLevelSets { - pub fn new(sess: &Session) -> LintLevelSets { + pub fn new(sess: &Session, lint_store: &LintStore) -> LintLevelSets { let mut me = LintLevelSets { list: Vec::new(), lint_cap: Level::Forbid, }; - me.process_command_line(sess); + me.process_command_line(sess, lint_store); return me } - pub fn builder(sess: &Session) -> LintLevelsBuilder<'_> { - LintLevelsBuilder::new(sess, LintLevelSets::new(sess)) + pub fn builder<'a>( + sess: &'a Session, + warn_about_weird_lints: bool, + store: &LintStore, + ) -> LintLevelsBuilder<'a> { + LintLevelsBuilder::new(sess, warn_about_weird_lints, LintLevelSets::new(sess, store)) } - fn process_command_line(&mut self, sess: &Session) { - let store = sess.lint_store.borrow(); + fn process_command_line(&mut self, sess: &Session, store: &LintStore) { let mut specs = FxHashMap::default(); self.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid); @@ -161,14 +164,18 @@ } impl<'a> LintLevelsBuilder<'a> { - pub fn new(sess: &'a Session, sets: LintLevelSets) -> LintLevelsBuilder<'a> { + pub fn new( + sess: &'a Session, + warn_about_weird_lints: bool, + sets: LintLevelSets, + ) -> LintLevelsBuilder<'a> { assert_eq!(sets.list.len(), 1); LintLevelsBuilder { sess, sets, cur: 0, id_to_set: Default::default(), - warn_about_weird_lints: sess.buffered_lints.borrow().is_some(), + warn_about_weird_lints, } } @@ -186,9 +193,8 @@ /// #[allow] /// /// Don't forget to call `pop`! - pub fn push(&mut self, attrs: &[ast::Attribute]) -> BuilderPush { + pub fn push(&mut self, attrs: &[ast::Attribute], store: &LintStore) -> BuilderPush { let mut specs = FxHashMap::default(); - let store = self.sess.lint_store.borrow(); let sess = self.sess; let bad_attr = |span| { struct_span_err!(sess, span, E0452, "malformed lint attribute input") @@ -202,11 +208,7 @@ let meta = unwrap_or!(attr.meta(), continue); attr::mark_used(attr); - let mut metas = if let Some(metas) = meta.meta_item_list() { - metas - } else { - continue; - }; + let mut metas = unwrap_or!(meta.meta_item_list(), continue); if metas.is_empty() { // FIXME (#55112): issue unused-attributes lint for `#[level()]` @@ -218,7 +220,7 @@ let mut reason = None; let tail_li = &metas[metas.len()-1]; if let Some(item) = tail_li.meta_item() { - match item.node { + match item.kind { ast::MetaItemKind::Word => {} // actual lint names handled later ast::MetaItemKind::NameValue(ref name_value) => { if item.path == sym::reason { @@ -226,7 +228,7 @@ metas = &metas[0..metas.len()-1]; // FIXME (#55112): issue unused-attributes lint if we thereby // don't have any lint names (`#[level(reason = "foo")]`) - if let ast::LitKind::Str(rationale, _) = name_value.node { + if let ast::LitKind::Str(rationale, _) = name_value.kind { if !self.sess.features_untracked().lint_reasons { feature_gate::emit_feature_err( &self.sess.parse_sess, @@ -264,7 +266,7 @@ let mut err = bad_attr(sp); let mut add_label = true; if let Some(item) = li.meta_item() { - if let ast::MetaItemKind::NameValue(_) = item.node { + if let ast::MetaItemKind::NameValue(_) = item.kind { if item.path == sym::reason { err.span_label(sp, "reason in lint attribute must come last"); add_label = false; @@ -286,7 +288,7 @@ tool_ident.span, E0710, "an unknown tool name found in scoped lint: `{}`", - meta_item.path + pprust::path_to_string(&meta_item.path), ); continue; } @@ -526,9 +528,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let LintLevelMap { ref sets, ref id_to_set, @@ -567,9 +567,7 @@ impl<HCX> HashStable<HCX> for LintId { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut HCX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { self.lint_name_raw().hash_stable(hcx, hasher); } }
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 5b490b7..11d0d0d 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs
@@ -28,6 +28,7 @@ use crate::hir; use crate::lint::builtin::BuiltinLintDiagnostics; use crate::lint::builtin::parser::{ILL_FORMED_ATTRIBUTE_INPUT, META_VARIABLE_MISUSE}; +use crate::lint::builtin::parser::INCOMPLETE_INCLUDE; use crate::session::{Session, DiagnosticMessageId}; use crate::ty::TyCtxt; use crate::ty::query::Providers; @@ -38,13 +39,13 @@ use syntax::source_map::{MultiSpan, ExpnKind, DesugaringKind}; use syntax::early_buffered_lints::BufferedEarlyLintId; use syntax::edition::Edition; -use syntax::ext::base::MacroKind; use syntax::symbol::{Symbol, sym}; +use syntax_pos::hygiene::MacroKind; use syntax_pos::Span; pub use crate::lint::context::{LateContext, EarlyContext, LintContext, LintStore, check_crate, check_ast_crate, late_lint_mod, CheckLintNameResult, - FutureIncompatibleInfo, BufferedEarlyLint,}; + BufferedEarlyLint,}; /// Specification of a single lint. #[derive(Copy, Clone, Debug)] @@ -75,14 +76,41 @@ /// `true` if this lint is reported even inside expansions of external macros. pub report_in_external_macro: bool, + + pub future_incompatible: Option<FutureIncompatibleInfo>, + + pub is_plugin: bool, +} + +/// Extra information for a future incompatibility lint. +#[derive(Copy, Clone, Debug)] +pub struct FutureIncompatibleInfo { + /// e.g., a URL for an issue/PR/RFC or error code + pub reference: &'static str, + /// If this is an edition fixing lint, the edition in which + /// this lint becomes obsolete + pub edition: Option<Edition>, } impl Lint { + pub const fn default_fields_for_macro() -> Self { + Lint { + name: "", + default_level: Level::Forbid, + desc: "", + edition_lint_opts: None, + is_plugin: false, + report_in_external_macro: false, + future_incompatible: None, + } + } + /// Returns the `rust::lint::Lint` for a `syntax::early_buffered_lints::BufferedEarlyLintId`. pub fn from_parser_lint_id(lint_id: BufferedEarlyLintId) -> &'static Self { match lint_id { BufferedEarlyLintId::IllFormedAttributeInput => ILL_FORMED_ATTRIBUTE_INPUT, BufferedEarlyLintId::MetaVariableMisuse => META_VARIABLE_MISUSE, + BufferedEarlyLintId::IncompleteInclude => INCOMPLETE_INCLUDE, } } @@ -103,18 +131,21 @@ #[macro_export] macro_rules! declare_lint { ($vis: vis $NAME: ident, $Level: ident, $desc: expr) => ( - declare_lint!{$vis $NAME, $Level, $desc, false} + declare_lint!( + $vis $NAME, $Level, $desc, + ); ); - ($vis: vis $NAME: ident, $Level: ident, $desc: expr, report_in_external_macro: $rep: expr) => ( - declare_lint!{$vis $NAME, $Level, $desc, $rep} - ); - ($vis: vis $NAME: ident, $Level: ident, $desc: expr, $external: expr) => ( + ($vis: vis $NAME: ident, $Level: ident, $desc: expr, + $(@future_incompatible = $fi:expr;)? $($v:ident),*) => ( $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { name: stringify!($NAME), default_level: $crate::lint::$Level, desc: $desc, edition_lint_opts: None, - report_in_external_macro: $external, + is_plugin: false, + $($v: true,)* + $(future_incompatible: Some($fi),)* + ..$crate::lint::Lint::default_fields_for_macro() }; ); ($vis: vis $NAME: ident, $Level: ident, $desc: expr, @@ -126,6 +157,7 @@ desc: $desc, edition_lint_opts: Some(($lint_edition, $crate::lint::Level::$edition_level)), report_in_external_macro: false, + is_plugin: false, }; ); } @@ -154,6 +186,8 @@ desc: $desc, edition_lint_opts: None, report_in_external_macro: $external, + future_incompatible: None, + is_plugin: true, }; ); } @@ -171,14 +205,6 @@ pub trait LintPass { fn name(&self) -> &'static str; - - /// Gets descriptions of the lints this `LintPass` object can emit. - /// - /// N.B., there is no enforcement that the object only emits lints it registered. - /// And some `rustc` internal `LintPass`es register lints to be emitted by other - /// parts of the compiler. If you want enforced access restrictions for your - /// `Lint`, make it a private `static` item in its own module. - fn get_lints(&self) -> LintArray; } /// Implements `LintPass for $name` with the given list of `Lint` statics. @@ -187,7 +213,9 @@ ($name:ident => [$($lint:expr),* $(,)?]) => { impl LintPass for $name { fn name(&self) -> &'static str { stringify!($name) } - fn get_lints(&self) -> LintArray { $crate::lint_array!($($lint),*) } + } + impl $name { + pub fn get_lints() -> LintArray { $crate::lint_array!($($lint),*) } } }; } @@ -285,9 +313,6 @@ macro_rules! declare_late_lint_pass { ([], [$hir:tt], [$($methods:tt)*]) => ( pub trait LateLintPass<'a, $hir>: LintPass { - fn fresh_late_pass(&self) -> LateLintPassObject { - panic!() - } expand_lint_pass_methods!(&LateContext<'a, $hir>, [$($methods)*]); } ) @@ -325,6 +350,12 @@ $($passes: $constructor,)* } } + + $v fn get_lints() -> LintArray { + let mut lints = Vec::new(); + $(lints.extend_from_slice(&$passes::get_lints());)* + lints + } } impl<'a, 'tcx> LateLintPass<'a, 'tcx> for $name { @@ -335,12 +366,6 @@ fn name(&self) -> &'static str { panic!() } - - fn get_lints(&self) -> LintArray { - let mut lints = Vec::new(); - $(lints.extend_from_slice(&self.$passes.get_lints());)* - lints - } } ) } @@ -452,6 +477,12 @@ $($passes: $constructor,)* } } + + $v fn get_lints() -> LintArray { + let mut lints = Vec::new(); + $(lints.extend_from_slice(&$passes::get_lints());)* + lints + } } impl EarlyLintPass for $name { @@ -462,12 +493,6 @@ fn name(&self) -> &'static str { panic!() } - - fn get_lints(&self) -> LintArray { - let mut lints = Vec::new(); - $(lints.extend_from_slice(&self.$passes.get_lints());)* - lints - } } ) } @@ -618,13 +643,29 @@ } } - pub fn take(&mut self, id: ast::NodeId) -> Vec<BufferedEarlyLint> { + fn take(&mut self, id: ast::NodeId) -> Vec<BufferedEarlyLint> { self.map.remove(&id).unwrap_or_default() } - pub fn get_any(&self) -> Option<&[BufferedEarlyLint]> { - let key = self.map.keys().next().map(|k| *k); - key.map(|k| &self.map[&k][..]) + pub fn buffer_lint<S: Into<MultiSpan>>( + &mut self, + lint: &'static Lint, + id: ast::NodeId, + sp: S, + msg: &str, + ) { + self.add_lint(lint, id, sp.into(), msg, BuiltinLintDiagnostics::Normal) + } + + pub fn buffer_lint_with_diagnostic<S: Into<MultiSpan>>( + &mut self, + lint: &'static Lint, + id: ast::NodeId, + sp: S, + msg: &str, + diagnostic: BuiltinLintDiagnostics, + ) { + self.add_lint(lint, id, sp.into(), msg, diagnostic) } } @@ -647,9 +688,8 @@ }; // Check for future incompatibility lints and issue a stronger warning. - let lints = sess.lint_store.borrow(); let lint_id = LintId::of(lint); - let future_incompatible = lints.future_incompatible(lint_id); + let future_incompatible = lint.future_incompatible; // If this code originates in a foreign macro, aka something that this crate // did not itself author, then it's likely that there's nothing this crate @@ -753,13 +793,15 @@ fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap { assert_eq!(cnum, LOCAL_CRATE); + let store = &tcx.lint_store; let mut builder = LintLevelMapBuilder { - levels: LintLevelSets::builder(tcx.sess), + levels: LintLevelSets::builder(tcx.sess, false, &store), tcx: tcx, + store: store, }; let krate = tcx.hir().krate(); - let push = builder.levels.push(&krate.attrs); + let push = builder.levels.push(&krate.attrs, &store); builder.levels.register_id(hir::CRATE_HIR_ID); for macro_def in &krate.exported_macros { builder.levels.register_id(macro_def.hir_id); @@ -770,19 +812,20 @@ tcx.arena.alloc(builder.levels.build_map()) } -struct LintLevelMapBuilder<'tcx> { +struct LintLevelMapBuilder<'a, 'tcx> { levels: levels::LintLevelsBuilder<'tcx>, tcx: TyCtxt<'tcx>, + store: &'a LintStore, } -impl LintLevelMapBuilder<'tcx> { +impl LintLevelMapBuilder<'_, '_> { fn with_lint_attrs<F>(&mut self, id: hir::HirId, attrs: &[ast::Attribute], f: F) where F: FnOnce(&mut Self) { - let push = self.levels.push(attrs); + let push = self.levels.push(attrs, self.store); if push.changed { self.levels.register_id(id); } @@ -791,7 +834,7 @@ } } -impl intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> { +impl intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> { intravisit::NestedVisitorMap::All(&self.tcx.hir()) }
diff --git a/src/librustc/macros.rs b/src/librustc/macros.rs index 09fa924..256a08d 100644 --- a/src/librustc/macros.rs +++ b/src/librustc/macros.rs
@@ -97,9 +97,9 @@ where $($T: ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>>),* { #[inline] - fn hash_stable<W: ::rustc_data_structures::stable_hasher::StableHasherResult>(&self, - __ctx: &mut $crate::ich::StableHashingContext<'a>, - __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher<W>) { + fn hash_stable(&self, + __ctx: &mut $crate::ich::StableHashingContext<'a>, + __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { use $enum_path::*; ::std::mem::discriminant(self).hash_stable(__ctx, __hasher); @@ -128,9 +128,9 @@ where $($T: ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>>),* { #[inline] - fn hash_stable<W: ::rustc_data_structures::stable_hasher::StableHasherResult>(&self, - __ctx: &mut $crate::ich::StableHashingContext<'a>, - __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher<W>) { + fn hash_stable(&self, + __ctx: &mut $crate::ich::StableHashingContext<'a>, + __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { let $struct_name { $(ref $field),* } = *self; @@ -153,9 +153,9 @@ where $($T: ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>>),* { #[inline] - fn hash_stable<W: ::rustc_data_structures::stable_hasher::StableHasherResult>(&self, - __ctx: &mut $crate::ich::StableHashingContext<'a>, - __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher<W>) { + fn hash_stable(&self, + __ctx: &mut $crate::ich::StableHashingContext<'a>, + __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { let $struct_name ( $(ref $field),* ) = *self; @@ -173,9 +173,9 @@ impl HashStable<StableHashingContext<'a>> for ::syntax::source_map::Spanned<$T> { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher) { self.node.hash_stable(hcx, hasher); self.span.hash_stable(hcx, hasher); }
diff --git a/src/librustc/middle/borrowck.rs b/src/librustc/middle/borrowck.rs deleted file mode 100644 index 60c24ee..0000000 --- a/src/librustc/middle/borrowck.rs +++ /dev/null
@@ -1,31 +0,0 @@ -use crate::ich::StableHashingContext; - -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, - StableHasherResult}; - -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] -pub enum SignalledError { SawSomeError, NoErrorsSeen } - -impl Default for SignalledError { - fn default() -> SignalledError { - SignalledError::NoErrorsSeen - } -} - -impl_stable_hash_for!(enum self::SignalledError { SawSomeError, NoErrorsSeen }); - -#[derive(Debug, Default, RustcEncodable, RustcDecodable)] -pub struct BorrowCheckResult { - pub signalled_any_error: SignalledError, -} - -impl<'a> HashStable<StableHashingContext<'a>> for BorrowCheckResult { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { - let BorrowCheckResult { - ref signalled_any_error, - } = *self; - signalled_any_error.hash_stable(hcx, hasher); - } -}
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index de84fcd7..1f40862 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs
@@ -16,7 +16,7 @@ use syntax::symbol::Symbol; use syntax_pos::Span; use rustc_target::spec::Target; -use rustc_data_structures::sync::{self, MetadataRef, Lrc}; +use rustc_data_structures::sync::{self, MetadataRef}; use rustc_macros::HashStable; pub use self::NativeLibraryKind::*; @@ -32,6 +32,12 @@ pub rmeta: Option<(PathBuf, PathKind)>, } +impl CrateSource { + pub fn paths(&self) -> impl Iterator<Item = &PathBuf> { + self.dylib.iter().chain(self.rlib.iter()).chain(self.rmeta.iter()).map(|p| &p.0) + } +} + #[derive(RustcEncodable, RustcDecodable, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, HashStable)] pub enum DepKind { @@ -96,6 +102,8 @@ NativeStaticNobundle, /// macOS-specific NativeFramework, + /// Windows dynamic library without import library. + NativeRawDylib, /// default way to specify a dynamic library NativeUnknown, } @@ -109,7 +117,7 @@ pub wasm_import_module: Option<Symbol>, } -#[derive(Clone, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] pub struct ForeignModule { pub foreign_items: Vec<DefId>, pub def_id: DefId, @@ -126,10 +134,17 @@ /// used to select the extern with the shortest path pub path_len: usize, + /// Crate that depends on this crate + pub dependency_of: CrateNum, +} + +impl ExternCrate { /// If true, then this crate is the crate named by the extern /// crate referenced above. If false, then this crate is a dep /// of the crate. - pub direct: bool, + pub fn is_direct(&self) -> bool { + self.dependency_of == LOCAL_CRATE + } } #[derive(Copy, Clone, Debug, HashStable)] @@ -141,9 +156,7 @@ /// such ids DefId, ), - // Crate is loaded by `use`. - Use, - /// Crate is implicitly loaded by an absolute path. + /// Crate is implicitly loaded by a path resolving through extern prelude. Path, } @@ -178,6 +191,8 @@ -> Result<MetadataRef, String>; } +pub type MetadataLoaderDyn = dyn MetadataLoader + Sync; + /// A store of Rust crates, through which their metadata can be accessed. /// /// Note that this trait should probably not be expanding today. All new @@ -188,20 +203,20 @@ /// (it'd break incremental compilation) and should only be called pre-HIR (e.g. /// during resolve) pub trait CrateStore { - fn crate_data_as_rc_any(&self, krate: CrateNum) -> Lrc<dyn Any>; + fn crate_data_as_any(&self, cnum: CrateNum) -> &dyn Any; // resolve fn def_key(&self, def: DefId) -> DefKey; fn def_path(&self, def: DefId) -> hir_map::DefPath; fn def_path_hash(&self, def: DefId) -> hir_map::DefPathHash; - fn def_path_table(&self, cnum: CrateNum) -> Lrc<DefPathTable>; + fn def_path_table(&self, cnum: CrateNum) -> &DefPathTable; // "queries" used in resolve that aren't tracked for incremental compilation fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol; fn crate_is_private_dep_untracked(&self, cnum: CrateNum) -> bool; fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> CrateDisambiguator; fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh; - fn extern_mod_stmt_cnum_untracked(&self, emod_id: ast::NodeId) -> Option<CrateNum>; + fn crate_host_hash_untracked(&self, cnum: CrateNum) -> Option<Svh>; fn item_generics_cloned_untracked(&self, def: DefId, sess: &Session) -> ty::Generics; fn postorder_cnums_untracked(&self) -> Vec<CrateNum>;
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs deleted file mode 100644 index d4805a7..0000000 --- a/src/librustc/middle/dead.rs +++ /dev/null
@@ -1,682 +0,0 @@ -// This implements the dead-code warning pass. It follows middle::reachable -// closely. The idea is that all reachable symbols are live, codes called -// from live codes are live, and everything else is dead. - -use crate::hir::Node; -use crate::hir::{self, PatKind, TyKind}; -use crate::hir::intravisit::{self, Visitor, NestedVisitorMap}; -use crate::hir::itemlikevisit::ItemLikeVisitor; - -use crate::hir::def::{CtorOf, Res, DefKind}; -use crate::hir::CodegenFnAttrFlags; -use crate::hir::def_id::{DefId, LOCAL_CRATE}; -use crate::lint; -use crate::middle::privacy; -use crate::ty::{self, DefIdTree, TyCtxt}; -use crate::util::nodemap::FxHashSet; - -use rustc_data_structures::fx::FxHashMap; - -use syntax::{ast, attr}; -use syntax::symbol::sym; -use syntax_pos; - -// Any local node that may call something in its body block should be -// explored. For example, if it's a live Node::Item that is a -// function, then we should explore its block to check for codes that -// may need to be marked as live. -fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { - match tcx.hir().find(hir_id) { - Some(Node::Item(..)) | - Some(Node::ImplItem(..)) | - Some(Node::ForeignItem(..)) | - Some(Node::TraitItem(..)) | - Some(Node::Variant(..)) | - Some(Node::AnonConst(..)) | - Some(Node::Pat(..)) => true, - _ => false - } -} - -struct MarkSymbolVisitor<'a, 'tcx> { - worklist: Vec<hir::HirId>, - tcx: TyCtxt<'tcx>, - tables: &'a ty::TypeckTables<'tcx>, - live_symbols: FxHashSet<hir::HirId>, - repr_has_repr_c: bool, - in_pat: bool, - inherited_pub_visibility: bool, - ignore_variant_stack: Vec<DefId>, - // maps from tuple struct constructors to tuple struct items - struct_constructors: FxHashMap<hir::HirId, hir::HirId>, -} - -impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { - fn check_def_id(&mut self, def_id: DefId) { - if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) { - if should_explore(self.tcx, hir_id) || self.struct_constructors.contains_key(&hir_id) { - self.worklist.push(hir_id); - } - self.live_symbols.insert(hir_id); - } - } - - fn insert_def_id(&mut self, def_id: DefId) { - if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) { - debug_assert!(!should_explore(self.tcx, hir_id)); - self.live_symbols.insert(hir_id); - } - } - - fn handle_res(&mut self, res: Res) { - match res { - Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) - | Res::Def(DefKind::TyAlias, _) => { - self.check_def_id(res.def_id()); - } - _ if self.in_pat => {}, - Res::PrimTy(..) | Res::SelfCtor(..) | - Res::Local(..) => {} - Res::Def(DefKind::Ctor(CtorOf::Variant, ..), ctor_def_id) => { - let variant_id = self.tcx.parent(ctor_def_id).unwrap(); - let enum_id = self.tcx.parent(variant_id).unwrap(); - self.check_def_id(enum_id); - if !self.ignore_variant_stack.contains(&ctor_def_id) { - self.check_def_id(variant_id); - } - } - Res::Def(DefKind::Variant, variant_id) => { - let enum_id = self.tcx.parent(variant_id).unwrap(); - self.check_def_id(enum_id); - if !self.ignore_variant_stack.contains(&variant_id) { - self.check_def_id(variant_id); - } - } - Res::SelfTy(t, i) => { - if let Some(t) = t { - self.check_def_id(t); - } - if let Some(i) = i { - self.check_def_id(i); - } - } - Res::ToolMod | Res::NonMacroAttr(..) | Res::Err => {} - _ => { - self.check_def_id(res.def_id()); - } - } - } - - fn lookup_and_handle_method(&mut self, id: hir::HirId) { - if let Some(def_id) = self.tables.type_dependent_def_id(id) { - self.check_def_id(def_id); - } else { - bug!("no type-dependent def for method"); - } - } - - fn handle_field_access(&mut self, lhs: &hir::Expr, hir_id: hir::HirId) { - match self.tables.expr_ty_adjusted(lhs).sty { - ty::Adt(def, _) => { - let index = self.tcx.field_index(hir_id, self.tables); - self.insert_def_id(def.non_enum_variant().fields[index].did); - } - ty::Tuple(..) => {} - _ => span_bug!(lhs.span, "named field access on non-ADT"), - } - } - - fn handle_field_pattern_match(&mut self, lhs: &hir::Pat, res: Res, pats: &[hir::FieldPat]) { - let variant = match self.tables.node_type(lhs.hir_id).sty { - ty::Adt(adt, _) => adt.variant_of_res(res), - _ => span_bug!(lhs.span, "non-ADT in struct pattern") - }; - for pat in pats { - if let PatKind::Wild = pat.pat.node { - continue; - } - let index = self.tcx.field_index(pat.hir_id, self.tables); - self.insert_def_id(variant.fields[index].did); - } - } - - fn mark_live_symbols(&mut self) { - let mut scanned = FxHashSet::default(); - while let Some(id) = self.worklist.pop() { - if !scanned.insert(id) { - continue - } - - // in the case of tuple struct constructors we want to check the item, not the generated - // tuple struct constructor function - let id = self.struct_constructors.get(&id).cloned().unwrap_or(id); - - if let Some(node) = self.tcx.hir().find(id) { - self.live_symbols.insert(id); - self.visit_node(node); - } - } - } - - fn visit_node(&mut self, node: Node<'tcx>) { - let had_repr_c = self.repr_has_repr_c; - self.repr_has_repr_c = false; - let had_inherited_pub_visibility = self.inherited_pub_visibility; - self.inherited_pub_visibility = false; - match node { - Node::Item(item) => { - match item.node { - hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => { - let def_id = self.tcx.hir().local_def_id(item.hir_id); - let def = self.tcx.adt_def(def_id); - self.repr_has_repr_c = def.repr.c(); - - intravisit::walk_item(self, &item); - } - hir::ItemKind::Enum(..) => { - self.inherited_pub_visibility = item.vis.node.is_pub(); - - intravisit::walk_item(self, &item); - } - hir::ItemKind::ForeignMod(..) => {} - _ => { - intravisit::walk_item(self, &item); - } - } - } - Node::TraitItem(trait_item) => { - intravisit::walk_trait_item(self, trait_item); - } - Node::ImplItem(impl_item) => { - intravisit::walk_impl_item(self, impl_item); - } - Node::ForeignItem(foreign_item) => { - intravisit::walk_foreign_item(self, &foreign_item); - } - _ => {} - } - self.repr_has_repr_c = had_repr_c; - self.inherited_pub_visibility = had_inherited_pub_visibility; - } - - fn mark_as_used_if_union(&mut self, adt: &ty::AdtDef, fields: &hir::HirVec<hir::Field>) { - if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did.is_local() { - for field in fields { - let index = self.tcx.field_index(field.hir_id, self.tables); - self.insert_def_id(adt.non_enum_variant().fields[index].did); - } - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::None - } - - fn visit_nested_body(&mut self, body: hir::BodyId) { - let old_tables = self.tables; - self.tables = self.tcx.body_tables(body); - let body = self.tcx.hir().body(body); - self.visit_body(body); - self.tables = old_tables; - } - - fn visit_variant_data(&mut self, def: &'tcx hir::VariantData, _: ast::Name, - _: &hir::Generics, _: hir::HirId, _: syntax_pos::Span) { - let has_repr_c = self.repr_has_repr_c; - let inherited_pub_visibility = self.inherited_pub_visibility; - let live_fields = def.fields().iter().filter(|f| { - has_repr_c || inherited_pub_visibility || f.vis.node.is_pub() - }); - self.live_symbols.extend(live_fields.map(|f| f.hir_id)); - - intravisit::walk_struct_def(self, def); - } - - fn visit_expr(&mut self, expr: &'tcx hir::Expr) { - match expr.node { - hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => { - let res = self.tables.qpath_res(qpath, expr.hir_id); - self.handle_res(res); - } - hir::ExprKind::MethodCall(..) => { - self.lookup_and_handle_method(expr.hir_id); - } - hir::ExprKind::Field(ref lhs, ..) => { - self.handle_field_access(&lhs, expr.hir_id); - } - hir::ExprKind::Struct(_, ref fields, _) => { - if let ty::Adt(ref adt, _) = self.tables.expr_ty(expr).sty { - self.mark_as_used_if_union(adt, fields); - } - } - _ => () - } - - intravisit::walk_expr(self, expr); - } - - fn visit_arm(&mut self, arm: &'tcx hir::Arm) { - if arm.pats.len() == 1 { - let variants = arm.pats[0].necessary_variants(); - - // Inside the body, ignore constructions of variants - // necessary for the pattern to match. Those construction sites - // can't be reached unless the variant is constructed elsewhere. - let len = self.ignore_variant_stack.len(); - self.ignore_variant_stack.extend_from_slice(&variants); - intravisit::walk_arm(self, arm); - self.ignore_variant_stack.truncate(len); - } else { - intravisit::walk_arm(self, arm); - } - } - - fn visit_pat(&mut self, pat: &'tcx hir::Pat) { - match pat.node { - PatKind::Struct(ref path, ref fields, _) => { - let res = self.tables.qpath_res(path, pat.hir_id); - self.handle_field_pattern_match(pat, res, fields); - } - PatKind::Path(ref qpath) => { - let res = self.tables.qpath_res(qpath, pat.hir_id); - self.handle_res(res); - } - _ => () - } - - self.in_pat = true; - intravisit::walk_pat(self, pat); - self.in_pat = false; - } - - fn visit_path(&mut self, path: &'tcx hir::Path, _: hir::HirId) { - self.handle_res(path.res); - intravisit::walk_path(self, path); - } - - fn visit_ty(&mut self, ty: &'tcx hir::Ty) { - match ty.node { - TyKind::Def(item_id, _) => { - let item = self.tcx.hir().expect_item(item_id.id); - intravisit::walk_item(self, item); - } - _ => () - } - intravisit::walk_ty(self, ty); - } - - fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { - self.live_symbols.insert(c.hir_id); - intravisit::walk_anon_const(self, c); - } -} - -fn has_allow_dead_code_or_lang_attr( - tcx: TyCtxt<'_>, - id: hir::HirId, - attrs: &[ast::Attribute], -) -> bool { - if attr::contains_name(attrs, sym::lang) { - return true; - } - - // Stable attribute for #[lang = "panic_impl"] - if attr::contains_name(attrs, sym::panic_handler) { - return true; - } - - // (To be) stable attribute for #[lang = "oom"] - if attr::contains_name(attrs, sym::alloc_error_handler) { - return true; - } - - let def_id = tcx.hir().local_def_id(id); - let cg_attrs = tcx.codegen_fn_attrs(def_id); - - // #[used], #[no_mangle], #[export_name], etc also keeps the item alive - // forcefully, e.g., for placing it in a specific section. - if cg_attrs.contains_extern_indicator() || - cg_attrs.flags.contains(CodegenFnAttrFlags::USED) { - return true; - } - - tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow -} - -// This visitor seeds items that -// 1) We want to explicitly consider as live: -// * Item annotated with #[allow(dead_code)] -// - This is done so that if we want to suppress warnings for a -// group of dead functions, we only have to annotate the "root". -// For example, if both `f` and `g` are dead and `f` calls `g`, -// then annotating `f` with `#[allow(dead_code)]` will suppress -// warning for both `f` and `g`. -// * Item annotated with #[lang=".."] -// - This is because lang items are always callable from elsewhere. -// or -// 2) We are not sure to be live or not -// * Implementation of a trait method -struct LifeSeeder<'k, 'tcx> { - worklist: Vec<hir::HirId>, - krate: &'k hir::Crate, - tcx: TyCtxt<'tcx>, - // see `MarkSymbolVisitor::struct_constructors` - struct_constructors: FxHashMap<hir::HirId, hir::HirId>, -} - -impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> { - fn visit_item(&mut self, item: &hir::Item) { - let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx, - item.hir_id, - &item.attrs); - if allow_dead_code { - self.worklist.push(item.hir_id); - } - match item.node { - hir::ItemKind::Enum(ref enum_def, _) => { - if allow_dead_code { - self.worklist.extend(enum_def.variants.iter().map(|variant| variant.id)); - } - - for variant in &enum_def.variants { - if let Some(ctor_hir_id) = variant.data.ctor_hir_id() { - self.struct_constructors.insert(ctor_hir_id, variant.id); - } - } - } - hir::ItemKind::Trait(.., ref trait_item_refs) => { - for trait_item_ref in trait_item_refs { - let trait_item = self.krate.trait_item(trait_item_ref.id); - match trait_item.node { - hir::TraitItemKind::Const(_, Some(_)) | - hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => { - if has_allow_dead_code_or_lang_attr(self.tcx, - trait_item.hir_id, - &trait_item.attrs) { - self.worklist.push(trait_item.hir_id); - } - } - _ => {} - } - } - } - hir::ItemKind::Impl(.., ref opt_trait, _, ref impl_item_refs) => { - for impl_item_ref in impl_item_refs { - let impl_item = self.krate.impl_item(impl_item_ref.id); - if opt_trait.is_some() || - has_allow_dead_code_or_lang_attr(self.tcx, - impl_item.hir_id, - &impl_item.attrs) { - self.worklist.push(impl_item_ref.id.hir_id); - } - } - } - hir::ItemKind::Struct(ref variant_data, _) => { - if let Some(ctor_hir_id) = variant_data.ctor_hir_id() { - self.struct_constructors.insert(ctor_hir_id, item.hir_id); - } - } - _ => () - } - } - - fn visit_trait_item(&mut self, _item: &hir::TraitItem) { - // ignore: we are handling this in `visit_item` above - } - - fn visit_impl_item(&mut self, _item: &hir::ImplItem) { - // ignore: we are handling this in `visit_item` above - } -} - -fn create_and_seed_worklist<'tcx>( - tcx: TyCtxt<'tcx>, - access_levels: &privacy::AccessLevels, - krate: &hir::Crate, -) -> (Vec<hir::HirId>, FxHashMap<hir::HirId, hir::HirId>) { - let worklist = access_levels.map.iter().filter_map(|(&id, level)| { - if level >= &privacy::AccessLevel::Reachable { - Some(id) - } else { - None - } - }).chain( - // Seed entry point - tcx.entry_fn(LOCAL_CRATE).map(|(def_id, _)| tcx.hir().as_local_hir_id(def_id).unwrap()) - ).collect::<Vec<_>>(); - - // Seed implemented trait items - let mut life_seeder = LifeSeeder { - worklist, - krate, - tcx, - struct_constructors: Default::default(), - }; - krate.visit_all_item_likes(&mut life_seeder); - - (life_seeder.worklist, life_seeder.struct_constructors) -} - -fn find_live<'tcx>( - tcx: TyCtxt<'tcx>, - access_levels: &privacy::AccessLevels, - krate: &hir::Crate, -) -> FxHashSet<hir::HirId> { - let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels, krate); - let mut symbol_visitor = MarkSymbolVisitor { - worklist, - tcx, - tables: &ty::TypeckTables::empty(None), - live_symbols: Default::default(), - repr_has_repr_c: false, - in_pat: false, - inherited_pub_visibility: false, - ignore_variant_stack: vec![], - struct_constructors, - }; - symbol_visitor.mark_live_symbols(); - symbol_visitor.live_symbols -} - -struct DeadVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - live_symbols: FxHashSet<hir::HirId>, -} - -impl DeadVisitor<'tcx> { - fn should_warn_about_item(&mut self, item: &hir::Item) -> bool { - let should_warn = match item.node { - hir::ItemKind::Static(..) - | hir::ItemKind::Const(..) - | hir::ItemKind::Fn(..) - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..) => true, - _ => false - }; - should_warn && !self.symbol_is_live(item.hir_id) - } - - fn should_warn_about_field(&mut self, field: &hir::StructField) -> bool { - let field_type = self.tcx.type_of(self.tcx.hir().local_def_id(field.hir_id)); - !field.is_positional() - && !self.symbol_is_live(field.hir_id) - && !field_type.is_phantom_data() - && !has_allow_dead_code_or_lang_attr(self.tcx, field.hir_id, &field.attrs) - } - - fn should_warn_about_variant(&mut self, variant: &hir::Variant) -> bool { - !self.symbol_is_live(variant.id) - && !has_allow_dead_code_or_lang_attr(self.tcx, - variant.id, - &variant.attrs) - } - - fn should_warn_about_foreign_item(&mut self, fi: &hir::ForeignItem) -> bool { - !self.symbol_is_live(fi.hir_id) - && !has_allow_dead_code_or_lang_attr(self.tcx, fi.hir_id, &fi.attrs) - } - - // id := HIR id of an item's definition. - fn symbol_is_live( - &mut self, - id: hir::HirId, - ) -> bool { - if self.live_symbols.contains(&id) { - return true; - } - // If it's a type whose items are live, then it's live, too. - // This is done to handle the case where, for example, the static - // method of a private type is used, but the type itself is never - // called directly. - let def_id = self.tcx.hir().local_def_id(id); - let inherent_impls = self.tcx.inherent_impls(def_id); - for &impl_did in inherent_impls.iter() { - for &item_did in &self.tcx.associated_item_def_ids(impl_did)[..] { - if let Some(item_hir_id) = self.tcx.hir().as_local_hir_id(item_did) { - if self.live_symbols.contains(&item_hir_id) { - return true; - } - } - } - } - false - } - - fn warn_dead_code(&mut self, - id: hir::HirId, - span: syntax_pos::Span, - name: ast::Name, - node_type: &str, - participle: &str) { - if !name.as_str().starts_with("_") { - self.tcx - .lint_hir(lint::builtin::DEAD_CODE, - id, - span, - &format!("{} is never {}: `{}`", - node_type, participle, name)); - } - } -} - -impl Visitor<'tcx> for DeadVisitor<'tcx> { - /// Walk nested items in place so that we don't report dead-code - /// on inner functions when the outer function is already getting - /// an error. We could do this also by checking the parents, but - /// this is how the code is setup and it seems harmless enough. - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::All(&self.tcx.hir()) - } - - fn visit_item(&mut self, item: &'tcx hir::Item) { - if self.should_warn_about_item(item) { - // For items that have a definition with a signature followed by a - // block, point only at the signature. - let span = match item.node { - hir::ItemKind::Fn(..) | - hir::ItemKind::Mod(..) | - hir::ItemKind::Enum(..) | - hir::ItemKind::Struct(..) | - hir::ItemKind::Union(..) | - hir::ItemKind::Trait(..) | - hir::ItemKind::Impl(..) => self.tcx.sess.source_map().def_span(item.span), - _ => item.span, - }; - let participle = match item.node { - hir::ItemKind::Struct(..) => "constructed", // Issue #52325 - _ => "used" - }; - self.warn_dead_code( - item.hir_id, - span, - item.ident.name, - item.node.descriptive_variant(), - participle, - ); - } else { - // Only continue if we didn't warn - intravisit::walk_item(self, item); - } - } - - fn visit_variant(&mut self, - variant: &'tcx hir::Variant, - g: &'tcx hir::Generics, - id: hir::HirId) { - if self.should_warn_about_variant(&variant) { - self.warn_dead_code(variant.id, variant.span, variant.ident.name, - "variant", "constructed"); - } else { - intravisit::walk_variant(self, variant, g, id); - } - } - - fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem) { - if self.should_warn_about_foreign_item(fi) { - self.warn_dead_code(fi.hir_id, fi.span, fi.ident.name, - fi.node.descriptive_variant(), "used"); - } - intravisit::walk_foreign_item(self, fi); - } - - fn visit_struct_field(&mut self, field: &'tcx hir::StructField) { - if self.should_warn_about_field(&field) { - self.warn_dead_code(field.hir_id, field.span, field.ident.name, "field", "used"); - } - intravisit::walk_struct_field(self, field); - } - - fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { - match impl_item.node { - hir::ImplItemKind::Const(_, body_id) => { - if !self.symbol_is_live(impl_item.hir_id) { - self.warn_dead_code(impl_item.hir_id, - impl_item.span, - impl_item.ident.name, - "associated const", - "used"); - } - self.visit_nested_body(body_id) - } - hir::ImplItemKind::Method(_, body_id) => { - if !self.symbol_is_live(impl_item.hir_id) { - let span = self.tcx.sess.source_map().def_span(impl_item.span); - self.warn_dead_code(impl_item.hir_id, span, impl_item.ident.name, "method", - "used"); - } - self.visit_nested_body(body_id) - } - hir::ImplItemKind::OpaqueTy(..) | - hir::ImplItemKind::TyAlias(..) => {} - } - } - - // Overwrite so that we don't warn the trait item itself. - fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { - match trait_item.node { - hir::TraitItemKind::Const(_, Some(body_id)) | - hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(body_id)) => { - self.visit_nested_body(body_id) - } - hir::TraitItemKind::Const(_, None) | - hir::TraitItemKind::Method(_, hir::TraitMethod::Required(_)) | - hir::TraitItemKind::Type(..) => {} - } - } -} - -pub fn check_crate(tcx: TyCtxt<'_>) { - let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); - let krate = tcx.hir().krate(); - let live_symbols = find_live(tcx, access_levels, krate); - let mut visitor = DeadVisitor { - tcx, - live_symbols, - }; - intravisit::walk_crate(&mut visitor, krate); -}
diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs index 96b99fe..8b2bf55 100644 --- a/src/librustc/middle/dependency_format.rs +++ b/src/librustc/middle/dependency_format.rs
@@ -1,64 +1,10 @@ -//! Resolution of mixing rlibs and dylibs +//! Type definitions for learning about the dependency formats of all upstream +//! crates (rlibs/dylibs/oh my). //! -//! When producing a final artifact, such as a dynamic library, the compiler has -//! a choice between linking an rlib or linking a dylib of all upstream -//! dependencies. The linking phase must guarantee, however, that a library only -//! show up once in the object file. For example, it is illegal for library A to -//! be statically linked to B and C in separate dylibs, and then link B and C -//! into a crate D (because library A appears twice). -//! -//! The job of this module is to calculate what format each upstream crate -//! should be used when linking each output type requested in this session. This -//! generally follows this set of rules: -//! -//! 1. Each library must appear exactly once in the output. -//! 2. Each rlib contains only one library (it's just an object file) -//! 3. Each dylib can contain more than one library (due to static linking), -//! and can also bring in many dynamic dependencies. -//! -//! With these constraints in mind, it's generally a very difficult problem to -//! find a solution that's not "all rlibs" or "all dylibs". I have suspicions -//! that NP-ness may come into the picture here... -//! -//! The current selection algorithm below looks mostly similar to: -//! -//! 1. If static linking is required, then require all upstream dependencies -//! to be available as rlibs. If not, generate an error. -//! 2. If static linking is requested (generating an executable), then -//! attempt to use all upstream dependencies as rlibs. If any are not -//! found, bail out and continue to step 3. -//! 3. Static linking has failed, at least one library must be dynamically -//! linked. Apply a heuristic by greedily maximizing the number of -//! dynamically linked libraries. -//! 4. Each upstream dependency available as a dynamic library is -//! registered. The dependencies all propagate, adding to a map. It is -//! possible for a dylib to add a static library as a dependency, but it -//! is illegal for two dylibs to add the same static library as a -//! dependency. The same dylib can be added twice. Additionally, it is -//! illegal to add a static dependency when it was previously found as a -//! dylib (and vice versa) -//! 5. After all dynamic dependencies have been traversed, re-traverse the -//! remaining dependencies and add them statically (if they haven't been -//! added already). -//! -//! While not perfect, this algorithm should help support use-cases such as leaf -//! dependencies being static while the larger tree of inner dependencies are -//! all dynamic. This isn't currently very well battle tested, so it will likely -//! fall short in some use cases. -//! -//! Currently, there is no way to specify the preference of linkage with a -//! particular library (other than a global dynamic/static switch). -//! Additionally, the algorithm is geared towards finding *any* solution rather -//! than finding a number of solutions (there are normally quite a few). - -use crate::hir::def_id::CrateNum; +//! For all the gory details, see the provider of the `dependency_formats` +//! query. use crate::session::config; -use crate::ty::TyCtxt; -use crate::middle::cstore::{self, DepKind}; -use crate::middle::cstore::LinkagePreference::{self, RequireStatic, RequireDynamic}; -use crate::util::nodemap::FxHashMap; -use rustc_target::spec::PanicStrategy; /// A list of dependencies for a certain crate type. /// @@ -71,324 +17,12 @@ /// A mapping of all required dependencies for a particular flavor of output. /// /// This is local to the tcx, and is generally relevant to one session. -pub type Dependencies = FxHashMap<config::CrateType, DependencyList>; +pub type Dependencies = Vec<(config::CrateType, DependencyList)>; -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Debug, HashStable)] pub enum Linkage { NotLinked, IncludedFromDylib, Static, Dynamic, } - -pub fn calculate(tcx: TyCtxt<'_>) { - let sess = &tcx.sess; - let fmts = sess.crate_types.borrow().iter().map(|&ty| { - let linkage = calculate_type(tcx, ty); - verify_ok(tcx, &linkage); - (ty, linkage) - }).collect::<FxHashMap<_, _>>(); - sess.abort_if_errors(); - sess.dependency_formats.set(fmts); -} - -fn calculate_type(tcx: TyCtxt<'_>, ty: config::CrateType) -> DependencyList { - let sess = &tcx.sess; - - if !sess.opts.output_types.should_codegen() { - return Vec::new(); - } - - let preferred_linkage = match ty { - // cdylibs must have all static dependencies. - config::CrateType::Cdylib => Linkage::Static, - - // Generating a dylib without `-C prefer-dynamic` means that we're going - // to try to eagerly statically link all dependencies. This is normally - // done for end-product dylibs, not intermediate products. - config::CrateType::Dylib if !sess.opts.cg.prefer_dynamic => Linkage::Static, - config::CrateType::Dylib => Linkage::Dynamic, - - // If the global prefer_dynamic switch is turned off, or the final - // executable will be statically linked, prefer static crate linkage. - config::CrateType::Executable if !sess.opts.cg.prefer_dynamic || - sess.crt_static() => Linkage::Static, - config::CrateType::Executable => Linkage::Dynamic, - - // proc-macro crates are mostly cdylibs, but we also need metadata. - config::CrateType::ProcMacro => Linkage::Static, - - // No linkage happens with rlibs, we just needed the metadata (which we - // got long ago), so don't bother with anything. - config::CrateType::Rlib => Linkage::NotLinked, - - // staticlibs must have all static dependencies. - config::CrateType::Staticlib => Linkage::Static, - }; - - if preferred_linkage == Linkage::NotLinked { - // If the crate is not linked, there are no link-time dependencies. - return Vec::new(); - } - - if preferred_linkage == Linkage::Static { - // Attempt static linkage first. For dylibs and executables, we may be - // able to retry below with dynamic linkage. - if let Some(v) = attempt_static(tcx) { - return v; - } - - // Staticlibs, cdylibs, and static executables must have all static - // dependencies. If any are not found, generate some nice pretty errors. - if ty == config::CrateType::Cdylib || ty == config::CrateType::Staticlib || - (ty == config::CrateType::Executable && sess.crt_static() && - !sess.target.target.options.crt_static_allows_dylibs) { - for &cnum in tcx.crates().iter() { - if tcx.dep_kind(cnum).macros_only() { continue } - let src = tcx.used_crate_source(cnum); - if src.rlib.is_some() { continue } - sess.err(&format!("crate `{}` required to be available in rlib format, \ - but was not found in this form", - tcx.crate_name(cnum))); - } - return Vec::new(); - } - } - - let mut formats = FxHashMap::default(); - - // Sweep all crates for found dylibs. Add all dylibs, as well as their - // dependencies, ensuring there are no conflicts. The only valid case for a - // dependency to be relied upon twice is for both cases to rely on a dylib. - for &cnum in tcx.crates().iter() { - if tcx.dep_kind(cnum).macros_only() { continue } - let name = tcx.crate_name(cnum); - let src = tcx.used_crate_source(cnum); - if src.dylib.is_some() { - info!("adding dylib: {}", name); - add_library(tcx, cnum, RequireDynamic, &mut formats); - let deps = tcx.dylib_dependency_formats(cnum); - for &(depnum, style) in deps.iter() { - info!("adding {:?}: {}", style, tcx.crate_name(depnum)); - add_library(tcx, depnum, style, &mut formats); - } - } - } - - // Collect what we've got so far in the return vector. - let last_crate = tcx.crates().len(); - let mut ret = (1..last_crate+1).map(|cnum| { - match formats.get(&CrateNum::new(cnum)) { - Some(&RequireDynamic) => Linkage::Dynamic, - Some(&RequireStatic) => Linkage::IncludedFromDylib, - None => Linkage::NotLinked, - } - }).collect::<Vec<_>>(); - - // Run through the dependency list again, and add any missing libraries as - // static libraries. - // - // If the crate hasn't been included yet and it's not actually required - // (e.g., it's an allocator) then we skip it here as well. - for &cnum in tcx.crates().iter() { - let src = tcx.used_crate_source(cnum); - if src.dylib.is_none() && - !formats.contains_key(&cnum) && - tcx.dep_kind(cnum) == DepKind::Explicit { - assert!(src.rlib.is_some() || src.rmeta.is_some()); - info!("adding staticlib: {}", tcx.crate_name(cnum)); - add_library(tcx, cnum, RequireStatic, &mut formats); - ret[cnum.as_usize() - 1] = Linkage::Static; - } - } - - // We've gotten this far because we're emitting some form of a final - // artifact which means that we may need to inject dependencies of some - // form. - // - // Things like allocators and panic runtimes may not have been activated - // quite yet, so do so here. - activate_injected_dep(*sess.injected_panic_runtime.get(), &mut ret, - &|cnum| tcx.is_panic_runtime(cnum)); - - // When dylib B links to dylib A, then when using B we must also link to A. - // It could be the case, however, that the rlib for A is present (hence we - // found metadata), but the dylib for A has since been removed. - // - // For situations like this, we perform one last pass over the dependencies, - // making sure that everything is available in the requested format. - for (cnum, kind) in ret.iter().enumerate() { - let cnum = CrateNum::new(cnum + 1); - let src = tcx.used_crate_source(cnum); - match *kind { - Linkage::NotLinked | - Linkage::IncludedFromDylib => {} - Linkage::Static if src.rlib.is_some() => continue, - Linkage::Dynamic if src.dylib.is_some() => continue, - kind => { - let kind = match kind { - Linkage::Static => "rlib", - _ => "dylib", - }; - sess.err(&format!("crate `{}` required to be available in {} format, \ - but was not found in this form", - tcx.crate_name(cnum), kind)); - } - } - } - - ret -} - -fn add_library( - tcx: TyCtxt<'_>, - cnum: CrateNum, - link: LinkagePreference, - m: &mut FxHashMap<CrateNum, LinkagePreference>, -) { - match m.get(&cnum) { - Some(&link2) => { - // If the linkages differ, then we'd have two copies of the library - // if we continued linking. If the linkages are both static, then we - // would also have two copies of the library (static from two - // different locations). - // - // This error is probably a little obscure, but I imagine that it - // can be refined over time. - if link2 != link || link == RequireStatic { - tcx.sess.struct_err(&format!("cannot satisfy dependencies so `{}` only \ - shows up once", tcx.crate_name(cnum))) - .help("having upstream crates all available in one format \ - will likely make this go away") - .emit(); - } - } - None => { m.insert(cnum, link); } - } -} - -fn attempt_static(tcx: TyCtxt<'_>) -> Option<DependencyList> { - let sess = &tcx.sess; - let crates = cstore::used_crates(tcx, RequireStatic); - if !crates.iter().by_ref().all(|&(_, ref p)| p.is_some()) { - return None - } - - // All crates are available in an rlib format, so we're just going to link - // everything in explicitly so long as it's actually required. - let last_crate = tcx.crates().len(); - let mut ret = (1..last_crate+1).map(|cnum| { - if tcx.dep_kind(CrateNum::new(cnum)) == DepKind::Explicit { - Linkage::Static - } else { - Linkage::NotLinked - } - }).collect::<Vec<_>>(); - - // Our allocator/panic runtime may not have been linked above if it wasn't - // explicitly linked, which is the case for any injected dependency. Handle - // that here and activate them. - activate_injected_dep(*sess.injected_panic_runtime.get(), &mut ret, - &|cnum| tcx.is_panic_runtime(cnum)); - - Some(ret) -} - -// Given a list of how to link upstream dependencies so far, ensure that an -// injected dependency is activated. This will not do anything if one was -// transitively included already (e.g., via a dylib or explicitly so). -// -// If an injected dependency was not found then we're guaranteed the -// metadata::creader module has injected that dependency (not listed as -// a required dependency) in one of the session's field. If this field is not -// set then this compilation doesn't actually need the dependency and we can -// also skip this step entirely. -fn activate_injected_dep(injected: Option<CrateNum>, - list: &mut DependencyList, - replaces_injected: &dyn Fn(CrateNum) -> bool) { - for (i, slot) in list.iter().enumerate() { - let cnum = CrateNum::new(i + 1); - if !replaces_injected(cnum) { - continue - } - if *slot != Linkage::NotLinked { - return - } - } - if let Some(injected) = injected { - let idx = injected.as_usize() - 1; - assert_eq!(list[idx], Linkage::NotLinked); - list[idx] = Linkage::Static; - } -} - -// After the linkage for a crate has been determined we need to verify that -// there's only going to be one allocator in the output. -fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { - let sess = &tcx.sess; - if list.len() == 0 { - return - } - let mut panic_runtime = None; - for (i, linkage) in list.iter().enumerate() { - if let Linkage::NotLinked = *linkage { - continue - } - let cnum = CrateNum::new(i + 1); - - if tcx.is_panic_runtime(cnum) { - if let Some((prev, _)) = panic_runtime { - let prev_name = tcx.crate_name(prev); - let cur_name = tcx.crate_name(cnum); - sess.err(&format!("cannot link together two \ - panic runtimes: {} and {}", - prev_name, cur_name)); - } - panic_runtime = Some((cnum, tcx.panic_strategy(cnum))); - } - } - - // If we found a panic runtime, then we know by this point that it's the - // only one, but we perform validation here that all the panic strategy - // compilation modes for the whole DAG are valid. - if let Some((cnum, found_strategy)) = panic_runtime { - let desired_strategy = sess.panic_strategy(); - - // First up, validate that our selected panic runtime is indeed exactly - // our same strategy. - if found_strategy != desired_strategy { - sess.err(&format!("the linked panic runtime `{}` is \ - not compiled with this crate's \ - panic strategy `{}`", - tcx.crate_name(cnum), - desired_strategy.desc())); - } - - // Next up, verify that all other crates are compatible with this panic - // strategy. If the dep isn't linked, we ignore it, and if our strategy - // is abort then it's compatible with everything. Otherwise all crates' - // panic strategy must match our own. - for (i, linkage) in list.iter().enumerate() { - if let Linkage::NotLinked = *linkage { - continue - } - if desired_strategy == PanicStrategy::Abort { - continue - } - let cnum = CrateNum::new(i + 1); - let found_strategy = tcx.panic_strategy(cnum); - let is_compiler_builtins = tcx.is_compiler_builtins(cnum); - if is_compiler_builtins || desired_strategy == found_strategy { - continue - } - - sess.err(&format!("the crate `{}` is compiled with the \ - panic strategy `{}` which is \ - incompatible with this crate's \ - strategy of `{}`", - tcx.crate_name(cnum), - found_strategy.desc(), - desired_strategy.desc())); - } - } -}
diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs deleted file mode 100644 index ba27d33..0000000 --- a/src/librustc/middle/entry.rs +++ /dev/null
@@ -1,202 +0,0 @@ -use crate::hir::map as hir_map; -use crate::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE}; -use crate::session::{config, Session}; -use crate::session::config::EntryFnType; -use syntax::attr; -use syntax::entry::EntryPointType; -use syntax::symbol::sym; -use syntax_pos::Span; -use crate::hir::{HirId, Item, ItemKind, ImplItem, TraitItem}; -use crate::hir::itemlikevisit::ItemLikeVisitor; -use crate::ty::TyCtxt; -use crate::ty::query::Providers; - -struct EntryContext<'a, 'tcx> { - session: &'a Session, - - map: &'a hir_map::Map<'tcx>, - - /// The top-level function called `main`. - main_fn: Option<(HirId, Span)>, - - /// The function that has attribute named `main`. - attr_main_fn: Option<(HirId, Span)>, - - /// The function that has the attribute 'start' on it. - start_fn: Option<(HirId, Span)>, - - /// The functions that one might think are `main` but aren't, e.g. - /// main functions not defined at the top level. For diagnostics. - non_main_fns: Vec<(HirId, Span)> , -} - -impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> { - fn visit_item(&mut self, item: &'tcx Item) { - let def_id = self.map.local_def_id(item.hir_id); - let def_key = self.map.def_key(def_id); - let at_root = def_key.parent == Some(CRATE_DEF_INDEX); - find_item(item, self, at_root); - } - - fn visit_trait_item(&mut self, _trait_item: &'tcx TraitItem) { - // Entry fn is never a trait item. - } - - fn visit_impl_item(&mut self, _impl_item: &'tcx ImplItem) { - // Entry fn is never a trait item. - } -} - -fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(DefId, EntryFnType)> { - assert_eq!(cnum, LOCAL_CRATE); - - let any_exe = tcx.sess.crate_types.borrow().iter().any(|ty| { - *ty == config::CrateType::Executable - }); - if !any_exe { - // No need to find a main function. - return None; - } - - // If the user wants no main function at all, then stop here. - if attr::contains_name(&tcx.hir().krate().attrs, sym::no_main) { - return None; - } - - let mut ctxt = EntryContext { - session: tcx.sess, - map: tcx.hir(), - main_fn: None, - attr_main_fn: None, - start_fn: None, - non_main_fns: Vec::new(), - }; - - tcx.hir().krate().visit_all_item_likes(&mut ctxt); - - configure_main(tcx, &ctxt) -} - -// Beware, this is duplicated in `libsyntax/entry.rs`, so make sure to keep -// them in sync. -fn entry_point_type(item: &Item, at_root: bool) -> EntryPointType { - match item.node { - ItemKind::Fn(..) => { - if attr::contains_name(&item.attrs, sym::start) { - EntryPointType::Start - } else if attr::contains_name(&item.attrs, sym::main) { - EntryPointType::MainAttr - } else if item.ident.name == sym::main { - if at_root { - // This is a top-level function so can be `main`. - EntryPointType::MainNamed - } else { - EntryPointType::OtherMain - } - } else { - EntryPointType::None - } - } - _ => EntryPointType::None, - } -} - - -fn find_item(item: &Item, ctxt: &mut EntryContext<'_, '_>, at_root: bool) { - match entry_point_type(item, at_root) { - EntryPointType::MainNamed => { - if ctxt.main_fn.is_none() { - ctxt.main_fn = Some((item.hir_id, item.span)); - } else { - span_err!(ctxt.session, item.span, E0136, - "multiple `main` functions"); - } - }, - EntryPointType::OtherMain => { - ctxt.non_main_fns.push((item.hir_id, item.span)); - }, - EntryPointType::MainAttr => { - if ctxt.attr_main_fn.is_none() { - ctxt.attr_main_fn = Some((item.hir_id, item.span)); - } else { - struct_span_err!(ctxt.session, item.span, E0137, - "multiple functions with a `#[main]` attribute") - .span_label(item.span, "additional `#[main]` function") - .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[main]` function") - .emit(); - } - }, - EntryPointType::Start => { - if ctxt.start_fn.is_none() { - ctxt.start_fn = Some((item.hir_id, item.span)); - } else { - struct_span_err!(ctxt.session, item.span, E0138, "multiple `start` functions") - .span_label(ctxt.start_fn.unwrap().1, "previous `start` function here") - .span_label(item.span, "multiple `start` functions") - .emit(); - } - } - EntryPointType::None => (), - } -} - -fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) -> Option<(DefId, EntryFnType)> { - if let Some((hir_id, _)) = visitor.start_fn { - Some((tcx.hir().local_def_id(hir_id), EntryFnType::Start)) - } else if let Some((hir_id, _)) = visitor.attr_main_fn { - Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main)) - } else if let Some((hir_id, _)) = visitor.main_fn { - Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main)) - } else { - no_main_err(tcx, visitor); - None - } -} - -fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) { - // There is no main function. - let mut err = struct_err!(tcx.sess, E0601, - "`main` function not found in crate `{}`", tcx.crate_name(LOCAL_CRATE)); - let filename = &tcx.sess.local_crate_source_file; - let note = if !visitor.non_main_fns.is_empty() { - for &(_, span) in &visitor.non_main_fns { - err.span_note(span, "here is a function named `main`"); - } - err.note("you have one or more functions named `main` not defined at the crate level"); - err.help("either move the `main` function definitions or attach the `#[main]` attribute \ - to one of them"); - // There were some functions named `main` though. Try to give the user a hint. - format!("the main function must be defined at the crate level{}", - filename.as_ref().map(|f| format!(" (in `{}`)", f.display())).unwrap_or_default()) - } else if let Some(filename) = filename { - format!("consider adding a `main` function to `{}`", filename.display()) - } else { - String::from("consider adding a `main` function at the crate level") - }; - let sp = tcx.hir().krate().span; - // The file may be empty, which leads to the diagnostic machinery not emitting this - // note. This is a relatively simple way to detect that case and emit a span-less - // note instead. - if let Ok(_) = tcx.sess.source_map().lookup_line(sp.lo()) { - err.set_span(sp); - err.span_label(sp, ¬e); - } else { - err.note(¬e); - } - if tcx.sess.teach(&err.get_code().unwrap()) { - err.note("If you don't know the basics of Rust, you can go look to the Rust Book \ - to get started: https://doc.rust-lang.org/book/"); - } - err.emit(); -} - -pub fn find_entry_point(tcx: TyCtxt<'_>) -> Option<(DefId, EntryFnType)> { - tcx.entry_fn(LOCAL_CRATE) -} - -pub fn provide(providers: &mut Providers<'_>) { - *providers = Providers { - entry_fn, - ..*providers - }; -}
diff --git a/src/librustc/middle/exported_symbols.rs b/src/librustc/middle/exported_symbols.rs index 2027880..4d14299 100644 --- a/src/librustc/middle/exported_symbols.rs +++ b/src/librustc/middle/exported_symbols.rs
@@ -1,7 +1,6 @@ use crate::hir::def_id::{DefId, LOCAL_CRATE}; use crate::ich::StableHashingContext; -use rustc_data_structures::stable_hasher::{StableHasher, HashStable, - StableHasherResult}; +use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use std::cmp; use std::mem; use crate::ty::{self, TyCtxt}; @@ -94,9 +93,7 @@ } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ExportedSymbol<'tcx> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { ExportedSymbol::NonGeneric(def_id) => {
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index de6dada..bb7ac5d 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs
@@ -2,25 +2,20 @@ //! normal visitor, which just walks the entire body in one shot, the //! `ExprUseVisitor` determines how expressions are being used. -pub use self::LoanCause::*; pub use self::ConsumeMode::*; -pub use self::MoveReason::*; -pub use self::MatchMode::*; -use self::TrackMatchMode::*; use self::OverloadedCallType::*; -use crate::hir::def::{CtorOf, Res, DefKind}; +use crate::hir::def::Res; use crate::hir::def_id::DefId; use crate::hir::ptr::P; use crate::infer::InferCtxt; use crate::middle::mem_categorization as mc; use crate::middle::region; -use crate::ty::{self, DefIdTree, TyCtxt, adjustment}; +use crate::ty::{self, TyCtxt, adjustment}; use crate::hir::{self, PatKind}; use std::rc::Rc; use syntax_pos::Span; -use crate::util::nodemap::ItemLocalSet; /////////////////////////////////////////////////////////////////////////// // The Delegate trait @@ -30,161 +25,19 @@ pub trait Delegate<'tcx> { // The value found at `cmt` is either copied or moved, depending // on mode. - fn consume(&mut self, - consume_id: hir::HirId, - consume_span: Span, - cmt: &mc::cmt_<'tcx>, - mode: ConsumeMode); + fn consume(&mut self, cmt: &mc::cmt_<'tcx>, mode: ConsumeMode); - // The value found at `cmt` has been determined to match the - // pattern binding `matched_pat`, and its subparts are being - // copied or moved depending on `mode`. Note that `matched_pat` - // is called on all variant/structs in the pattern (i.e., the - // interior nodes of the pattern's tree structure) while - // consume_pat is called on the binding identifiers in the pattern - // (which are leaves of the pattern's tree structure). - // - // Note that variants/structs and identifiers are disjoint; thus - // `matched_pat` and `consume_pat` are never both called on the - // same input pattern structure (though of `consume_pat` can be - // called on a subpart of an input passed to `matched_pat). - fn matched_pat(&mut self, - matched_pat: &hir::Pat, - cmt: &mc::cmt_<'tcx>, - mode: MatchMode); - - // The value found at `cmt` is either copied or moved via the - // pattern binding `consume_pat`, depending on mode. - fn consume_pat(&mut self, - consume_pat: &hir::Pat, - cmt: &mc::cmt_<'tcx>, - mode: ConsumeMode); - - // The value found at `borrow` is being borrowed at the point - // `borrow_id` for the region `loan_region` with kind `bk`. - fn borrow(&mut self, - borrow_id: hir::HirId, - borrow_span: Span, - cmt: &mc::cmt_<'tcx>, - loan_region: ty::Region<'tcx>, - bk: ty::BorrowKind, - loan_cause: LoanCause); - - // The local variable `id` is declared but not initialized. - fn decl_without_init(&mut self, - id: hir::HirId, - span: Span); + // The value found at `cmt` is being borrowed with kind `bk`. + fn borrow(&mut self, cmt: &mc::cmt_<'tcx>, bk: ty::BorrowKind); // The path at `cmt` is being assigned to. - fn mutate(&mut self, - assignment_id: hir::HirId, - assignment_span: Span, - assignee_cmt: &mc::cmt_<'tcx>, - mode: MutateMode); - - // A nested closure or generator - only one layer deep. - fn nested_body(&mut self, _body_id: hir::BodyId) {} -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum LoanCause { - ClosureCapture(Span), - AddrOf, - AutoRef, - AutoUnsafe, - RefBinding, - OverloadedOperator, - ClosureInvocation, - ForLoop, - MatchDiscriminant + fn mutate(&mut self, assignee_cmt: &mc::cmt_<'tcx>); } #[derive(Copy, Clone, PartialEq, Debug)] pub enum ConsumeMode { Copy, // reference to x where x has a type that copies - Move(MoveReason), // reference to x where x has a type that moves -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum MoveReason { - DirectRefMove, - PatBindingMove, - CaptureMove, -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum MatchMode { - NonBindingMatch, - BorrowingMatch, - CopyingMatch, - MovingMatch, -} - -#[derive(Copy, Clone, PartialEq, Debug)] -enum TrackMatchMode { - Unknown, - Definite(MatchMode), - Conflicting, -} - -impl TrackMatchMode { - // Builds up the whole match mode for a pattern from its constituent - // parts. The lattice looks like this: - // - // Conflicting - // / \ - // / \ - // Borrowing Moving - // \ / - // \ / - // Copying - // | - // NonBinding - // | - // Unknown - // - // examples: - // - // * `(_, some_int)` pattern is Copying, since - // NonBinding + Copying => Copying - // - // * `(some_int, some_box)` pattern is Moving, since - // Copying + Moving => Moving - // - // * `(ref x, some_box)` pattern is Conflicting, since - // Borrowing + Moving => Conflicting - // - // Note that the `Unknown` and `Conflicting` states are - // represented separately from the other more interesting - // `Definite` states, which simplifies logic here somewhat. - fn lub(&mut self, mode: MatchMode) { - *self = match (*self, mode) { - // Note that clause order below is very significant. - (Unknown, new) => Definite(new), - (Definite(old), new) if old == new => Definite(old), - - (Definite(old), NonBindingMatch) => Definite(old), - (Definite(NonBindingMatch), new) => Definite(new), - - (Definite(old), CopyingMatch) => Definite(old), - (Definite(CopyingMatch), new) => Definite(new), - - (Definite(_), _) => Conflicting, - (Conflicting, _) => *self, - }; - } - - fn match_mode(&self) -> MatchMode { - match *self { - Unknown => NonBindingMatch, - Definite(mode) => mode, - Conflicting => { - // Conservatively return MovingMatch to let the - // compiler continue to make progress. - MovingMatch - } - } - } + Move, // reference to x where x has a type that moves } #[derive(Copy, Clone, PartialEq, Debug)] @@ -261,9 +114,6 @@ /// - `param_env` --- parameter environment for trait lookups (esp. pertaining to `Copy`) /// - `region_scope_tree` --- region scope tree for the code being analyzed /// - `tables` --- typeck results for the code being analyzed - /// - `rvalue_promotable_map` --- if you care about rvalue promotion, then provide - /// the map here (it can be computed with `tcx.rvalue_promotable_map(def_id)`). - /// `None` means that rvalues will be given more conservative lifetimes. /// /// See also `with_infer`, which is used *during* typeck. pub fn new( @@ -273,15 +123,13 @@ param_env: ty::ParamEnv<'tcx>, region_scope_tree: &'a region::ScopeTree, tables: &'a ty::TypeckTables<'tcx>, - rvalue_promotable_map: Option<&'tcx ItemLocalSet>, ) -> Self { ExprUseVisitor { mc: mc::MemCategorizationContext::new(tcx, param_env, body_owner, region_scope_tree, - tables, - rvalue_promotable_map), + tables), delegate, param_env, } @@ -317,16 +165,9 @@ let param_ty = return_if_err!(self.mc.pat_ty_adjusted(¶m.pat)); debug!("consume_body: param_ty = {:?}", param_ty); - let fn_body_scope_r = - self.tcx().mk_region(ty::ReScope( - region::Scope { - id: body.value.hir_id.local_id, - data: region::ScopeData::Node - })); let param_cmt = Rc::new(self.mc.cat_rvalue( param.hir_id, param.pat.span, - fn_body_scope_r, // Parameters live only as long as the fn body. param_ty)); self.walk_irrefutable_pat(param_cmt, ¶m.pat); @@ -339,15 +180,11 @@ self.mc.tcx } - fn delegate_consume(&mut self, - consume_id: hir::HirId, - consume_span: Span, - cmt: &mc::cmt_<'tcx>) { - debug!("delegate_consume(consume_id={}, cmt={:?})", - consume_id, cmt); + fn delegate_consume(&mut self, cmt: &mc::cmt_<'tcx>) { + debug!("delegate_consume(cmt={:?})", cmt); - let mode = copy_or_move(&self.mc, self.param_env, cmt, DirectRefMove); - self.delegate.consume(consume_id, consume_span, cmt, mode); + let mode = copy_or_move(&self.mc, self.param_env, cmt); + self.delegate.consume(cmt, mode); } fn consume_exprs(&mut self, exprs: &[hir::Expr]) { @@ -360,30 +197,21 @@ debug!("consume_expr(expr={:?})", expr); let cmt = return_if_err!(self.mc.cat_expr(expr)); - self.delegate_consume(expr.hir_id, expr.span, &cmt); + self.delegate_consume(&cmt); self.walk_expr(expr); } - fn mutate_expr(&mut self, - span: Span, - assignment_expr: &hir::Expr, - expr: &hir::Expr, - mode: MutateMode) { + fn mutate_expr(&mut self, expr: &hir::Expr) { let cmt = return_if_err!(self.mc.cat_expr(expr)); - self.delegate.mutate(assignment_expr.hir_id, span, &cmt, mode); + self.delegate.mutate(&cmt); self.walk_expr(expr); } - fn borrow_expr(&mut self, - expr: &hir::Expr, - r: ty::Region<'tcx>, - bk: ty::BorrowKind, - cause: LoanCause) { - debug!("borrow_expr(expr={:?}, r={:?}, bk={:?})", - expr, r, bk); + fn borrow_expr(&mut self, expr: &hir::Expr, bk: ty::BorrowKind) { + debug!("borrow_expr(expr={:?}, bk={:?})", expr, bk); let cmt = return_if_err!(self.mc.cat_expr(expr)); - self.delegate.borrow(expr.hir_id, expr.span, &cmt, r, bk, cause); + self.delegate.borrow(&cmt, bk); self.walk_expr(expr) } @@ -397,28 +225,28 @@ self.walk_adjustment(expr); - match expr.node { + match expr.kind { hir::ExprKind::Path(_) => { } hir::ExprKind::Type(ref subexpr, _) => { - self.walk_expr(&subexpr) + self.walk_expr(subexpr) } hir::ExprKind::Unary(hir::UnDeref, ref base) => { // *base - self.select_from_expr(&base); + self.select_from_expr(base); } hir::ExprKind::Field(ref base, _) => { // base.f - self.select_from_expr(&base); + self.select_from_expr(base); } hir::ExprKind::Index(ref lhs, ref rhs) => { // lhs[rhs] - self.select_from_expr(&lhs); - self.consume_expr(&rhs); + self.select_from_expr(lhs); + self.consume_expr(rhs); } hir::ExprKind::Call(ref callee, ref args) => { // callee(args) - self.walk_callee(expr, &callee); + self.walk_callee(expr, callee); self.consume_exprs(args); } @@ -436,14 +264,11 @@ hir::ExprKind::Match(ref discr, ref arms, _) => { let discr_cmt = Rc::new(return_if_err!(self.mc.cat_expr(&discr))); - let r = self.tcx().lifetimes.re_empty; - self.borrow_expr(&discr, r, ty::ImmBorrow, MatchDiscriminant); + self.borrow_expr(&discr, ty::ImmBorrow); // treatment of the discriminant is handled while walking the arms. for arm in arms { - let mode = self.arm_move_mode(discr_cmt.clone(), arm); - let mode = mode.match_mode(); - self.walk_arm(discr_cmt.clone(), arm, mode); + self.walk_arm(discr_cmt.clone(), arm); } } @@ -454,11 +279,8 @@ hir::ExprKind::AddrOf(m, ref base) => { // &base // make sure that the thing we are pointing out stays valid // for the lifetime `scope_r` of the resulting ptr: - let expr_ty = return_if_err!(self.mc.expr_ty(expr)); - if let ty::Ref(r, _, _) = expr_ty.sty { - let bk = ty::BorrowKind::from_mutbl(m); - self.borrow_expr(&base, r, bk, AddrOf); - } + let bk = ty::BorrowKind::from_mutbl(m); + self.borrow_expr(&base, bk); } hir::ExprKind::InlineAsm(ref ia, ref outputs, ref inputs) => { @@ -466,16 +288,7 @@ if o.is_indirect { self.consume_expr(output); } else { - self.mutate_expr( - output.span, - expr, - output, - if o.is_rw { - MutateMode::WriteAndRead - } else { - MutateMode::JustWrite - }, - ); + self.mutate_expr(output); } } self.consume_exprs(inputs); @@ -486,65 +299,64 @@ hir::ExprKind::Err => {} hir::ExprKind::Loop(ref blk, _, _) => { - self.walk_block(&blk); + self.walk_block(blk); } hir::ExprKind::Unary(_, ref lhs) => { - self.consume_expr(&lhs); + self.consume_expr(lhs); } hir::ExprKind::Binary(_, ref lhs, ref rhs) => { - self.consume_expr(&lhs); - self.consume_expr(&rhs); + self.consume_expr(lhs); + self.consume_expr(rhs); } hir::ExprKind::Block(ref blk, _) => { - self.walk_block(&blk); + self.walk_block(blk); } hir::ExprKind::Break(_, ref opt_expr) | hir::ExprKind::Ret(ref opt_expr) => { if let Some(ref expr) = *opt_expr { - self.consume_expr(&expr); + self.consume_expr(expr); } } hir::ExprKind::Assign(ref lhs, ref rhs) => { - self.mutate_expr(expr.span, expr, &lhs, MutateMode::JustWrite); - self.consume_expr(&rhs); + self.mutate_expr(lhs); + self.consume_expr(rhs); } hir::ExprKind::Cast(ref base, _) => { - self.consume_expr(&base); + self.consume_expr(base); } hir::ExprKind::DropTemps(ref expr) => { - self.consume_expr(&expr); + self.consume_expr(expr); } hir::ExprKind::AssignOp(_, ref lhs, ref rhs) => { if self.mc.tables.is_method_call(expr) { self.consume_expr(lhs); } else { - self.mutate_expr(expr.span, expr, &lhs, MutateMode::WriteAndRead); + self.mutate_expr(lhs); } - self.consume_expr(&rhs); + self.consume_expr(rhs); } hir::ExprKind::Repeat(ref base, _) => { - self.consume_expr(&base); + self.consume_expr(base); } - hir::ExprKind::Closure(_, _, body_id, fn_decl_span, _) => { - self.delegate.nested_body(body_id); + hir::ExprKind::Closure(_, _, _, fn_decl_span, _) => { self.walk_captures(expr, fn_decl_span); } hir::ExprKind::Box(ref base) => { - self.consume_expr(&base); + self.consume_expr(base); } hir::ExprKind::Yield(ref value, _) => { - self.consume_expr(&value); + self.consume_expr(value); } } } @@ -553,31 +365,19 @@ let callee_ty = return_if_err!(self.mc.expr_ty_adjusted(callee)); debug!("walk_callee: callee={:?} callee_ty={:?}", callee, callee_ty); - match callee_ty.sty { + match callee_ty.kind { ty::FnDef(..) | ty::FnPtr(_) => { self.consume_expr(callee); } ty::Error => { } _ => { if let Some(def_id) = self.mc.tables.type_dependent_def_id(call.hir_id) { - let call_scope = region::Scope { - id: call.hir_id.local_id, - data: region::ScopeData::Node - }; match OverloadedCallType::from_method_id(self.tcx(), def_id) { FnMutOverloadedCall => { - let call_scope_r = self.tcx().mk_region(ty::ReScope(call_scope)); - self.borrow_expr(callee, - call_scope_r, - ty::MutBorrow, - ClosureInvocation); + self.borrow_expr(callee, ty::MutBorrow); } FnOverloadedCall => { - let call_scope_r = self.tcx().mk_region(ty::ReScope(call_scope)); - self.borrow_expr(callee, - call_scope_r, - ty::ImmBorrow, - ClosureInvocation); + self.borrow_expr(callee, ty::ImmBorrow); } FnOnceOverloadedCall => self.consume_expr(callee), } @@ -590,7 +390,7 @@ } fn walk_stmt(&mut self, stmt: &hir::Stmt) { - match stmt.node { + match stmt.kind { hir::StmtKind::Local(ref local) => { self.walk_local(&local); } @@ -608,22 +408,14 @@ } fn walk_local(&mut self, local: &hir::Local) { - match local.init { - None => { - local.pat.each_binding(|_, hir_id, span, _| { - self.delegate.decl_without_init(hir_id, span); - }) - } - - Some(ref expr) => { - // Variable declarations with - // initializers are considered - // "assigns", which is handled by - // `walk_pat`: - self.walk_expr(&expr); - let init_cmt = Rc::new(return_if_err!(self.mc.cat_expr(&expr))); - self.walk_irrefutable_pat(init_cmt, &local.pat); - } + if let Some(ref expr) = local.init { + // Variable declarations with + // initializers are considered + // "assigns", which is handled by + // `walk_pat`: + self.walk_expr(&expr); + let init_cmt = Rc::new(return_if_err!(self.mc.cat_expr(&expr))); + self.walk_irrefutable_pat(init_cmt, &local.pat); } } @@ -658,7 +450,7 @@ // Select just those fields of the `with` // expression that will actually be used - match with_cmt.ty.sty { + match with_cmt.ty.kind { ty::Adt(adt, substs) if adt.is_struct() => { // Consume those fields of the with expression that are needed. for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() { @@ -673,7 +465,7 @@ with_field.ident, with_field.ty(self.tcx(), substs) ); - self.delegate_consume(with_expr.hir_id, with_expr.span, &cmt_field); + self.delegate_consume(&cmt_field); } } } @@ -708,7 +500,7 @@ adjustment::Adjust::Pointer(_) => { // Creating a closure/fn-pointer or unsizing consumes // the input and stores it into the resulting rvalue. - self.delegate_consume(expr.hir_id, expr.span, &cmt); + self.delegate_consume(&cmt); } adjustment::Adjust::Deref(None) => {} @@ -720,7 +512,7 @@ // this is an autoref of `x`. adjustment::Adjust::Deref(Some(ref deref)) => { let bk = ty::BorrowKind::from_mutbl(deref.mutbl); - self.delegate.borrow(expr.hir_id, expr.span, &cmt, deref.region, bk, AutoRef); + self.delegate.borrow(&cmt, bk); } adjustment::Adjust::Borrow(ref autoref) => { @@ -744,13 +536,8 @@ autoref); match *autoref { - adjustment::AutoBorrow::Ref(r, m) => { - self.delegate.borrow(expr.hir_id, - expr.span, - cmt_base, - r, - ty::BorrowKind::from_mutbl(m.into()), - AutoRef); + adjustment::AutoBorrow::Ref(_, m) => { + self.delegate.borrow(cmt_base, ty::BorrowKind::from_mutbl(m.into())); } adjustment::AutoBorrow::RawPtr(m) => { @@ -758,37 +545,14 @@ expr.hir_id, cmt_base); - // Converting from a &T to *T (or &mut T to *mut T) is - // treated as borrowing it for the enclosing temporary - // scope. - let r = self.tcx().mk_region(ty::ReScope( - region::Scope { - id: expr.hir_id.local_id, - data: region::ScopeData::Node - })); - self.delegate.borrow(expr.hir_id, - expr.span, - cmt_base, - r, - ty::BorrowKind::from_mutbl(m), - AutoUnsafe); + self.delegate.borrow(cmt_base, ty::BorrowKind::from_mutbl(m)); } } } - fn arm_move_mode(&mut self, discr_cmt: mc::cmt<'tcx>, arm: &hir::Arm) -> TrackMatchMode { - let mut mode = Unknown; - for pat in &arm.pats { - self.determine_pat_move_mode(discr_cmt.clone(), &pat, &mut mode); - } - mode - } - - fn walk_arm(&mut self, discr_cmt: mc::cmt<'tcx>, arm: &hir::Arm, mode: MatchMode) { - for pat in &arm.pats { - self.walk_pat(discr_cmt.clone(), &pat, mode); - } + fn walk_arm(&mut self, discr_cmt: mc::cmt<'tcx>, arm: &hir::Arm) { + self.walk_pat(discr_cmt.clone(), &arm.pat); if let Some(hir::Guard::If(ref e)) = arm.guard { self.consume_expr(e) @@ -800,55 +564,22 @@ /// Walks a pat that occurs in isolation (i.e., top-level of fn argument or /// let binding, and *not* a match arm or nested pat.) fn walk_irrefutable_pat(&mut self, cmt_discr: mc::cmt<'tcx>, pat: &hir::Pat) { - let mut mode = Unknown; - self.determine_pat_move_mode(cmt_discr.clone(), pat, &mut mode); - let mode = mode.match_mode(); - self.walk_pat(cmt_discr, pat, mode); + self.walk_pat(cmt_discr, pat); } - /// Identifies any bindings within `pat` and accumulates within - /// `mode` whether the overall pattern/match structure is a move, - /// copy, or borrow. - fn determine_pat_move_mode(&mut self, - cmt_discr: mc::cmt<'tcx>, - pat: &hir::Pat, - mode: &mut TrackMatchMode) { - debug!("determine_pat_move_mode cmt_discr={:?} pat={:?}", cmt_discr, pat); - return_if_err!(self.mc.cat_pattern(cmt_discr, pat, |cmt_pat, pat| { - if let PatKind::Binding(..) = pat.node { - let bm = *self.mc.tables.pat_binding_modes() - .get(pat.hir_id) - .expect("missing binding mode"); - match bm { - ty::BindByReference(..) => - mode.lub(BorrowingMatch), - ty::BindByValue(..) => { - match copy_or_move(&self.mc, self.param_env, &cmt_pat, PatBindingMove) { - Copy => mode.lub(CopyingMatch), - Move(..) => mode.lub(MovingMatch), - } - } - } - } - })); - } - - /// The core driver for walking a pattern; `match_mode` must be - /// established up front, e.g., via `determine_pat_move_mode` (see - /// also `walk_irrefutable_pat` for patterns that stand alone). - fn walk_pat(&mut self, cmt_discr: mc::cmt<'tcx>, pat: &hir::Pat, match_mode: MatchMode) { + /// The core driver for walking a pattern + fn walk_pat(&mut self, cmt_discr: mc::cmt<'tcx>, pat: &hir::Pat) { debug!("walk_pat(cmt_discr={:?}, pat={:?})", cmt_discr, pat); let tcx = self.tcx(); let ExprUseVisitor { ref mc, ref mut delegate, param_env } = *self; return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |cmt_pat, pat| { - if let PatKind::Binding(_, canonical_id, ..) = pat.node { + if let PatKind::Binding(_, canonical_id, ..) = pat.kind { debug!( - "walk_pat: binding cmt_pat={:?} pat={:?} match_mode={:?}", + "walk_pat: binding cmt_pat={:?} pat={:?}", cmt_pat, pat, - match_mode, ); if let Some(&bm) = mc.tables.pat_binding_modes().get(pat.hir_id) { debug!("walk_pat: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); @@ -861,21 +592,19 @@ // binding being produced. let def = Res::Local(canonical_id); if let Ok(ref binding_cmt) = mc.cat_res(pat.hir_id, pat.span, pat_ty, def) { - delegate.mutate(pat.hir_id, pat.span, binding_cmt, MutateMode::Init); + delegate.mutate(binding_cmt); } // It is also a borrow or copy/move of the value being matched. match bm { ty::BindByReference(m) => { - if let ty::Ref(r, _, _) = pat_ty.sty { - let bk = ty::BorrowKind::from_mutbl(m); - delegate.borrow(pat.hir_id, pat.span, &cmt_pat, r, bk, RefBinding); - } + let bk = ty::BorrowKind::from_mutbl(m); + delegate.borrow(&cmt_pat, bk); } ty::BindByValue(..) => { - let mode = copy_or_move(mc, param_env, &cmt_pat, PatBindingMove); + let mode = copy_or_move(mc, param_env, &cmt_pat); debug!("walk_pat binding consuming pat"); - delegate.consume_pat(pat, &cmt_pat, mode); + delegate.consume(&cmt_pat, mode); } } } else { @@ -883,45 +612,6 @@ } } })); - - // Do a second pass over the pattern, calling `matched_pat` on - // the interior nodes (enum variants and structs), as opposed - // to the above loop's visit of than the bindings that form - // the leaves of the pattern tree structure. - return_if_err!(mc.cat_pattern(cmt_discr, pat, |cmt_pat, pat| { - let qpath = match pat.node { - PatKind::Path(ref qpath) | - PatKind::TupleStruct(ref qpath, ..) | - PatKind::Struct(ref qpath, ..) => qpath, - _ => return - }; - let res = mc.tables.qpath_res(qpath, pat.hir_id); - match res { - Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_did) => { - let variant_did = mc.tcx.parent(variant_ctor_did).unwrap(); - let downcast_cmt = mc.cat_downcast_if_needed(pat, cmt_pat, variant_did); - - debug!("variantctor downcast_cmt={:?} pat={:?}", downcast_cmt, pat); - delegate.matched_pat(pat, &downcast_cmt, match_mode); - } - Res::Def(DefKind::Variant, variant_did) => { - let downcast_cmt = mc.cat_downcast_if_needed(pat, cmt_pat, variant_did); - - debug!("variant downcast_cmt={:?} pat={:?}", downcast_cmt, pat); - delegate.matched_pat(pat, &downcast_cmt, match_mode); - } - Res::Def(DefKind::Struct, _) - | Res::Def(DefKind::Ctor(..), _) - | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::TyAlias, _) - | Res::Def(DefKind::AssocTy, _) - | Res::SelfTy(..) => { - debug!("struct cmt_pat={:?} pat={:?}", cmt_pat, pat); - delegate.matched_pat(pat, &cmt_pat, match_mode); - } - _ => {} - } - })); } fn walk_captures(&mut self, closure_expr: &hir::Expr, fn_decl_span: Span) { @@ -929,7 +619,7 @@ let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id); if let Some(upvars) = self.tcx().upvars(closure_def_id) { - for (&var_id, upvar) in upvars.iter() { + for &var_id in upvars.keys() { let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_id }, closure_expr_id: closure_def_id.to_local(), @@ -940,19 +630,11 @@ var_id)); match upvar_capture { ty::UpvarCapture::ByValue => { - let mode = copy_or_move(&self.mc, - self.param_env, - &cmt_var, - CaptureMove); - self.delegate.consume(closure_expr.hir_id, upvar.span, &cmt_var, mode); + let mode = copy_or_move(&self.mc, self.param_env, &cmt_var); + self.delegate.consume(&cmt_var, mode); } ty::UpvarCapture::ByRef(upvar_borrow) => { - self.delegate.borrow(closure_expr.hir_id, - fn_decl_span, - &cmt_var, - upvar_borrow.region, - upvar_borrow.kind, - ClosureCapture(upvar.span)); + self.delegate.borrow(&cmt_var, upvar_borrow.kind); } } } @@ -975,10 +657,9 @@ mc: &mc::MemCategorizationContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, cmt: &mc::cmt_<'tcx>, - move_reason: MoveReason, ) -> ConsumeMode { if !mc.type_is_copy_modulo_regions(param_env, cmt.ty, cmt.span) { - Move(move_reason) + Move } else { Copy }
diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs deleted file mode 100644 index 1cc96c5..0000000 --- a/src/librustc/middle/intrinsicck.rs +++ /dev/null
@@ -1,170 +0,0 @@ -use crate::hir::def::{Res, DefKind}; -use crate::hir::def_id::DefId; -use crate::ty::{self, Ty, TyCtxt}; -use crate::ty::layout::{LayoutError, Pointer, SizeSkeleton, VariantIdx}; -use crate::ty::query::Providers; - -use rustc_target::spec::abi::Abi::RustIntrinsic; -use rustc_data_structures::indexed_vec::Idx; -use syntax_pos::{Span, sym}; -use crate::hir::intravisit::{self, Visitor, NestedVisitorMap}; -use crate::hir; - -fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: DefId) { - tcx.hir().visit_item_likes_in_module( - module_def_id, - &mut ItemVisitor { tcx }.as_deep_visitor() - ); -} - -pub fn provide(providers: &mut Providers<'_>) { - *providers = Providers { - check_mod_intrinsics, - ..*providers - }; -} - -struct ItemVisitor<'tcx> { - tcx: TyCtxt<'tcx>, -} - -struct ExprVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - tables: &'tcx ty::TypeckTables<'tcx>, - param_env: ty::ParamEnv<'tcx>, -} - -/// If the type is `Option<T>`, it will return `T`, otherwise -/// the type itself. Works on most `Option`-like types. -fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - let (def, substs) = match ty.sty { - ty::Adt(def, substs) => (def, substs), - _ => return ty - }; - - if def.variants.len() == 2 && !def.repr.c() && def.repr.int.is_none() { - let data_idx; - - let one = VariantIdx::new(1); - let zero = VariantIdx::new(0); - - if def.variants[zero].fields.is_empty() { - data_idx = one; - } else if def.variants[one].fields.is_empty() { - data_idx = zero; - } else { - return ty; - } - - if def.variants[data_idx].fields.len() == 1 { - return def.variants[data_idx].fields[0].ty(tcx, substs); - } - } - - ty -} - -impl ExprVisitor<'tcx> { - fn def_id_is_transmute(&self, def_id: DefId) -> bool { - self.tcx.fn_sig(def_id).abi() == RustIntrinsic && - self.tcx.item_name(def_id) == sym::transmute - } - - fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) { - let sk_from = SizeSkeleton::compute(from, self.tcx, self.param_env); - let sk_to = SizeSkeleton::compute(to, self.tcx, self.param_env); - - // Check for same size using the skeletons. - if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) { - if sk_from.same_size(sk_to) { - return; - } - - // Special-case transmutting from `typeof(function)` and - // `Option<typeof(function)>` to present a clearer error. - let from = unpack_option_like(self.tcx.global_tcx(), from); - if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (&from.sty, sk_to) { - if size_to == Pointer.size(&self.tcx) { - struct_span_err!(self.tcx.sess, span, E0591, - "can't transmute zero-sized type") - .note(&format!("source type: {}", from)) - .note(&format!("target type: {}", to)) - .help("cast with `as` to a pointer instead") - .emit(); - return; - } - } - } - - // Try to display a sensible error with as much information as possible. - let skeleton_string = |ty: Ty<'tcx>, sk| { - match sk { - Ok(SizeSkeleton::Known(size)) => { - format!("{} bits", size.bits()) - } - Ok(SizeSkeleton::Pointer { tail, .. }) => { - format!("pointer to `{}`", tail) - } - Err(LayoutError::Unknown(bad)) => { - if bad == ty { - "this type does not have a fixed size".to_owned() - } else { - format!("size can vary because of {}", bad) - } - } - Err(err) => err.to_string() - } - }; - - let mut err = struct_span_err!(self.tcx.sess, span, E0512, - "cannot transmute between types of different sizes, \ - or dependently-sized types"); - if from == to { - err.note(&format!("`{}` does not have a fixed size", from)); - } else { - err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) - .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); - } - err.emit() - } -} - -impl Visitor<'tcx> for ItemVisitor<'tcx> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::None - } - - fn visit_nested_body(&mut self, body_id: hir::BodyId) { - let owner_def_id = self.tcx.hir().body_owner_def_id(body_id); - let body = self.tcx.hir().body(body_id); - let param_env = self.tcx.param_env(owner_def_id); - let tables = self.tcx.typeck_tables_of(owner_def_id); - ExprVisitor { tcx: self.tcx, param_env, tables }.visit_body(body); - self.visit_body(body); - } -} - -impl Visitor<'tcx> for ExprVisitor<'tcx> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::None - } - - fn visit_expr(&mut self, expr: &'tcx hir::Expr) { - let res = if let hir::ExprKind::Path(ref qpath) = expr.node { - self.tables.qpath_res(qpath, expr.hir_id) - } else { - Res::Err - }; - if let Res::Def(DefKind::Fn, did) = res { - if self.def_id_is_transmute(did) { - let typ = self.tables.node_type(expr.hir_id); - let sig = typ.fn_sig(self.tcx); - let from = sig.inputs().skip_binder()[0]; - let to = *sig.output().skip_binder(); - self.check_transmute(expr.span, from, to); - } - } - - intravisit::walk_expr(self, expr); - } -}
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index c5d9a72..41f02a8 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs
@@ -13,6 +13,7 @@ use crate::hir::check_attr::Target; use crate::ty::{self, TyCtxt}; use crate::middle::weak_lang_items; +use crate::middle::cstore::ExternCrate; use crate::util::nodemap::FxHashMap; use syntax::ast; @@ -182,16 +183,39 @@ E0152, "duplicate lang item found: `{}`.", name), - None => self.tcx.sess.struct_err(&format!( - "duplicate lang item in crate `{}`: `{}`.", - self.tcx.crate_name(item_def_id.krate), - name)), + None => { + match self.tcx.extern_crate(item_def_id) { + Some(ExternCrate {dependency_of, ..}) => { + self.tcx.sess.struct_err(&format!( + "duplicate lang item in crate `{}` (which `{}` depends on): `{}`.", + self.tcx.crate_name(item_def_id.krate), + self.tcx.crate_name(*dependency_of), + name)) + }, + _ => { + self.tcx.sess.struct_err(&format!( + "duplicate lang item in crate `{}`: `{}`.", + self.tcx.crate_name(item_def_id.krate), + name)) + } + } + }, }; if let Some(span) = self.tcx.hir().span_if_local(original_def_id) { span_note!(&mut err, span, "first defined here."); } else { - err.note(&format!("first defined in crate `{}`.", + match self.tcx.extern_crate(original_def_id) { + Some(ExternCrate {dependency_of, ..}) => { + err.note(&format!( + "first defined in crate `{}` (which `{}` depends on).", + self.tcx.crate_name(original_def_id.krate), + self.tcx.crate_name(*dependency_of))); + }, + _ => { + err.note(&format!("first defined in crate `{}`.", self.tcx.crate_name(original_def_id.krate))); + } + } } err.emit(); } @@ -273,6 +297,10 @@ SizedTraitLangItem, "sized", sized_trait, Target::Trait; UnsizeTraitLangItem, "unsize", unsize_trait, Target::Trait; + // trait injected by #[derive(PartialEq)], (i.e. "Partial EQ"). + StructuralPeqTraitLangItem, "structural_peq", structural_peq_trait, Target::Trait; + // trait injected by #[derive(Eq)], (i.e. "Total EQ"; no, I will not apologize). + StructuralTeqTraitLangItem, "structural_teq", structural_teq_trait, Target::Trait; CopyTraitLangItem, "copy", copy_trait, Target::Trait; CloneTraitLangItem, "clone", clone_trait, Target::Trait; SyncTraitLangItem, "sync", sync_trait, Target::Trait; @@ -342,6 +370,7 @@ PanicFnLangItem, "panic", panic_fn, Target::Fn; PanicBoundsCheckFnLangItem, "panic_bounds_check", panic_bounds_check_fn, Target::Fn; PanicInfoLangItem, "panic_info", panic_info, Target::Struct; + PanicLocationLangItem, "panic_location", panic_location, Target::Struct; PanicImplLangItem, "panic_impl", panic_impl, Target::Fn; // Libstd panic entry point. Necessary for const eval to be able to catch it BeginPanicFnLangItem, "begin_panic", begin_panic_fn, Target::Fn; @@ -356,7 +385,7 @@ EhPersonalityLangItem, "eh_personality", eh_personality, Target::Fn; EhUnwindResumeLangItem, "eh_unwind_resume", eh_unwind_resume, Target::Fn; - MSVCTryFilterLangItem, "msvc_try_filter", msvc_try_filter, Target::Static; + EhCatchTypeinfoLangItem, "eh_catch_typeinfo", eh_catch_typeinfo, Target::Static; OwnedBoxLangItem, "owned_box", owned_box, Target::Struct;
diff --git a/src/librustc/middle/lib_features.rs b/src/librustc/middle/lib_features.rs index 0d6d016..2d726fc 100644 --- a/src/librustc/middle/lib_features.rs +++ b/src/librustc/middle/lib_features.rs
@@ -59,7 +59,7 @@ attr.check_name(**stab_attr) }) { let meta_item = attr.meta(); - if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta_item { + if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta_item { let mut feature = None; let mut since = None; for meta in metas {
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs deleted file mode 100644 index 00013bf..0000000 --- a/src/librustc/middle/liveness.rs +++ /dev/null
@@ -1,1637 +0,0 @@ -//! A classic liveness analysis based on dataflow over the AST. Computes, -//! for each local variable in a function, whether that variable is live -//! at a given point. Program execution points are identified by their -//! IDs. -//! -//! # Basic idea -//! -//! The basic model is that each local variable is assigned an index. We -//! represent sets of local variables using a vector indexed by this -//! index. The value in the vector is either 0, indicating the variable -//! is dead, or the ID of an expression that uses the variable. -//! -//! We conceptually walk over the AST in reverse execution order. If we -//! find a use of a variable, we add it to the set of live variables. If -//! we find an assignment to a variable, we remove it from the set of live -//! variables. When we have to merge two flows, we take the union of -//! those two flows -- if the variable is live on both paths, we simply -//! pick one ID. In the event of loops, we continue doing this until a -//! fixed point is reached. -//! -//! ## Checking initialization -//! -//! At the function entry point, all variables must be dead. If this is -//! not the case, we can report an error using the ID found in the set of -//! live variables, which identifies a use of the variable which is not -//! dominated by an assignment. -//! -//! ## Checking moves -//! -//! After each explicit move, the variable must be dead. -//! -//! ## Computing last uses -//! -//! Any use of the variable where the variable is dead afterwards is a -//! last use. -//! -//! # Implementation details -//! -//! The actual implementation contains two (nested) walks over the AST. -//! The outer walk has the job of building up the ir_maps instance for the -//! enclosing function. On the way down the tree, it identifies those AST -//! nodes and variable IDs that will be needed for the liveness analysis -//! and assigns them contiguous IDs. The liveness ID for an AST node is -//! called a `live_node` (it's a newtype'd `u32`) and the ID for a variable -//! is called a `variable` (another newtype'd `u32`). -//! -//! On the way back up the tree, as we are about to exit from a function -//! declaration we allocate a `liveness` instance. Now that we know -//! precisely how many nodes and variables we need, we can allocate all -//! the various arrays that we will need to precisely the right size. We then -//! perform the actual propagation on the `liveness` instance. -//! -//! This propagation is encoded in the various `propagate_through_*()` -//! methods. It effectively does a reverse walk of the AST; whenever we -//! reach a loop node, we iterate until a fixed point is reached. -//! -//! ## The `RWU` struct -//! -//! At each live node `N`, we track three pieces of information for each -//! variable `V` (these are encapsulated in the `RWU` struct): -//! -//! - `reader`: the `LiveNode` ID of some node which will read the value -//! that `V` holds on entry to `N`. Formally: a node `M` such -//! that there exists a path `P` from `N` to `M` where `P` does not -//! write `V`. If the `reader` is `invalid_node()`, then the current -//! value will never be read (the variable is dead, essentially). -//! -//! - `writer`: the `LiveNode` ID of some node which will write the -//! variable `V` and which is reachable from `N`. Formally: a node `M` -//! such that there exists a path `P` from `N` to `M` and `M` writes -//! `V`. If the `writer` is `invalid_node()`, then there is no writer -//! of `V` that follows `N`. -//! -//! - `used`: a boolean value indicating whether `V` is *used*. We -//! distinguish a *read* from a *use* in that a *use* is some read that -//! is not just used to generate a new value. For example, `x += 1` is -//! a read but not a use. This is used to generate better warnings. -//! -//! ## Special Variables -//! -//! We generate various special variables for various, well, special purposes. -//! These are described in the `specials` struct: -//! -//! - `exit_ln`: a live node that is generated to represent every 'exit' from -//! the function, whether it be by explicit return, panic, or other means. -//! -//! - `fallthrough_ln`: a live node that represents a fallthrough -//! -//! - `clean_exit_var`: a synthetic variable that is only 'read' from the -//! fallthrough node. It is only live if the function could converge -//! via means other than an explicit `return` expression. That is, it is -//! only dead if the end of the function's block can never be reached. -//! It is the responsibility of typeck to ensure that there are no -//! `return` expressions in a function declared as diverging. - -use self::LiveNodeKind::*; -use self::VarKind::*; - -use crate::hir::def::*; -use crate::hir::Node; -use crate::hir::ptr::P; -use crate::ty::{self, TyCtxt}; -use crate::ty::query::Providers; -use crate::lint; -use crate::util::nodemap::{HirIdMap, HirIdSet}; - -use errors::Applicability; -use std::collections::{BTreeMap, VecDeque}; -use std::{fmt, u32}; -use std::io::prelude::*; -use std::io; -use std::rc::Rc; -use syntax::ast; -use syntax::symbol::{kw, sym}; -use syntax_pos::Span; - -use crate::hir; -use crate::hir::{Expr, HirId}; -use crate::hir::def_id::DefId; -use crate::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap}; - -#[derive(Copy, Clone, PartialEq)] -struct Variable(u32); - -#[derive(Copy, Clone, PartialEq)] -struct LiveNode(u32); - -impl Variable { - fn get(&self) -> usize { self.0 as usize } -} - -impl LiveNode { - fn get(&self) -> usize { self.0 as usize } -} - -#[derive(Copy, Clone, PartialEq, Debug)] -enum LiveNodeKind { - UpvarNode(Span), - ExprNode(Span), - VarDefNode(Span), - ExitNode -} - -fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String { - let cm = tcx.sess.source_map(); - match lnk { - UpvarNode(s) => { - format!("Upvar node [{}]", cm.span_to_string(s)) - } - ExprNode(s) => { - format!("Expr node [{}]", cm.span_to_string(s)) - } - VarDefNode(s) => { - format!("Var def node [{}]", cm.span_to_string(s)) - } - ExitNode => "Exit node".to_owned(), - } -} - -impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::OnlyBodies(&self.tcx.hir()) - } - - fn visit_fn(&mut self, fk: FnKind<'tcx>, fd: &'tcx hir::FnDecl, - b: hir::BodyId, s: Span, id: HirId) { - visit_fn(self, fk, fd, b, s, id); - } - - fn visit_local(&mut self, l: &'tcx hir::Local) { visit_local(self, l); } - fn visit_expr(&mut self, ex: &'tcx Expr) { visit_expr(self, ex); } - fn visit_arm(&mut self, a: &'tcx hir::Arm) { visit_arm(self, a); } -} - -fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: DefId) { - tcx.hir().visit_item_likes_in_module( - module_def_id, - &mut IrMaps::new(tcx, module_def_id).as_deep_visitor(), - ); -} - -pub fn provide(providers: &mut Providers<'_>) { - *providers = Providers { - check_mod_liveness, - ..*providers - }; -} - -impl fmt::Debug for LiveNode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ln({})", self.get()) - } -} - -impl fmt::Debug for Variable { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "v({})", self.get()) - } -} - -// ______________________________________________________________________ -// Creating ir_maps -// -// This is the first pass and the one that drives the main -// computation. It walks up and down the IR once. On the way down, -// we count for each function the number of variables as well as -// liveness nodes. A liveness node is basically an expression or -// capture clause that does something of interest: either it has -// interesting control flow or it uses/defines a local variable. -// -// On the way back up, at each function node we create liveness sets -// (we now know precisely how big to make our various vectors and so -// forth) and then do the data-flow propagation to compute the set -// of live variables at each program point. -// -// Finally, we run back over the IR one last time and, using the -// computed liveness, check various safety conditions. For example, -// there must be no live nodes at the definition site for a variable -// unless it has an initializer. Similarly, each non-mutable local -// variable must not be assigned if there is some successor -// assignment. And so forth. - -impl LiveNode { - fn is_valid(&self) -> bool { - self.0 != u32::MAX - } -} - -fn invalid_node() -> LiveNode { LiveNode(u32::MAX) } - -struct CaptureInfo { - ln: LiveNode, - var_hid: HirId -} - -#[derive(Copy, Clone, Debug)] -struct LocalInfo { - id: HirId, - name: ast::Name, - is_shorthand: bool, -} - -#[derive(Copy, Clone, Debug)] -enum VarKind { - Param(HirId, ast::Name), - Local(LocalInfo), - CleanExit -} - -struct IrMaps<'tcx> { - tcx: TyCtxt<'tcx>, - body_owner: DefId, - num_live_nodes: usize, - num_vars: usize, - live_node_map: HirIdMap<LiveNode>, - variable_map: HirIdMap<Variable>, - capture_info_map: HirIdMap<Rc<Vec<CaptureInfo>>>, - var_kinds: Vec<VarKind>, - lnks: Vec<LiveNodeKind>, -} - -impl IrMaps<'tcx> { - fn new(tcx: TyCtxt<'tcx>, body_owner: DefId) -> IrMaps<'tcx> { - IrMaps { - tcx, - body_owner, - num_live_nodes: 0, - num_vars: 0, - live_node_map: HirIdMap::default(), - variable_map: HirIdMap::default(), - capture_info_map: Default::default(), - var_kinds: Vec::new(), - lnks: Vec::new(), - } - } - - fn add_live_node(&mut self, lnk: LiveNodeKind) -> LiveNode { - let ln = LiveNode(self.num_live_nodes as u32); - self.lnks.push(lnk); - self.num_live_nodes += 1; - - debug!("{:?} is of kind {}", ln, - live_node_kind_to_string(lnk, self.tcx)); - - ln - } - - fn add_live_node_for_node(&mut self, hir_id: HirId, lnk: LiveNodeKind) { - let ln = self.add_live_node(lnk); - self.live_node_map.insert(hir_id, ln); - - debug!("{:?} is node {:?}", ln, hir_id); - } - - fn add_variable(&mut self, vk: VarKind) -> Variable { - let v = Variable(self.num_vars as u32); - self.var_kinds.push(vk); - self.num_vars += 1; - - match vk { - Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) => { - self.variable_map.insert(node_id, v); - }, - CleanExit => {} - } - - debug!("{:?} is {:?}", v, vk); - - v - } - - fn variable(&self, hir_id: HirId, span: Span) -> Variable { - match self.variable_map.get(&hir_id) { - Some(&var) => var, - None => { - span_bug!(span, "no variable registered for id {:?}", hir_id); - } - } - } - - fn variable_name(&self, var: Variable) -> String { - match self.var_kinds[var.get()] { - Local(LocalInfo { name, .. }) | Param(_, name) => { - name.to_string() - }, - CleanExit => "<clean-exit>".to_owned() - } - } - - fn variable_is_shorthand(&self, var: Variable) -> bool { - match self.var_kinds[var.get()] { - Local(LocalInfo { is_shorthand, .. }) => is_shorthand, - Param(..) | CleanExit => false - } - } - - fn set_captures(&mut self, hir_id: HirId, cs: Vec<CaptureInfo>) { - self.capture_info_map.insert(hir_id, Rc::new(cs)); - } - - fn lnk(&self, ln: LiveNode) -> LiveNodeKind { - self.lnks[ln.get()] - } -} - -fn visit_fn<'tcx>( - ir: &mut IrMaps<'tcx>, - fk: FnKind<'tcx>, - decl: &'tcx hir::FnDecl, - body_id: hir::BodyId, - sp: Span, - id: hir::HirId, -) { - debug!("visit_fn"); - - // swap in a new set of IR maps for this function body: - let def_id = ir.tcx.hir().local_def_id(id); - let mut fn_maps = IrMaps::new(ir.tcx, def_id); - - // Don't run unused pass for #[derive()] - if let FnKind::Method(..) = fk { - let parent = ir.tcx.hir().get_parent_item(id); - if let Some(Node::Item(i)) = ir.tcx.hir().find(parent) { - if i.attrs.iter().any(|a| a.check_name(sym::automatically_derived)) { - return; - } - } - } - - debug!("creating fn_maps: {:p}", &fn_maps); - - let body = ir.tcx.hir().body(body_id); - - for param in &body.params { - let is_shorthand = match param.pat.node { - crate::hir::PatKind::Struct(..) => true, - _ => false, - }; - param.pat.each_binding(|_bm, hir_id, _x, ident| { - debug!("adding parameters {:?}", hir_id); - let var = if is_shorthand { - Local(LocalInfo { - id: hir_id, - name: ident.name, - is_shorthand: true, - }) - } else { - Param(hir_id, ident.name) - }; - fn_maps.add_variable(var); - }) - }; - - // gather up the various local variables, significant expressions, - // and so forth: - intravisit::walk_fn(&mut fn_maps, fk, decl, body_id, sp, id); - - // compute liveness - let mut lsets = Liveness::new(&mut fn_maps, body_id); - let entry_ln = lsets.compute(&body.value); - - // check for various error conditions - lsets.visit_body(body); - lsets.warn_about_unused_args(body, entry_ln); -} - -fn add_from_pat<'tcx>(ir: &mut IrMaps<'tcx>, pat: &P<hir::Pat>) { - // For struct patterns, take note of which fields used shorthand - // (`x` rather than `x: x`). - let mut shorthand_field_ids = HirIdSet::default(); - let mut pats = VecDeque::new(); - pats.push_back(pat); - while let Some(pat) = pats.pop_front() { - use crate::hir::PatKind::*; - match pat.node { - Binding(_, _, _, ref inner_pat) => { - pats.extend(inner_pat.iter()); - } - Struct(_, ref fields, _) => { - for field in fields { - if field.is_shorthand { - shorthand_field_ids.insert(field.pat.hir_id); - } - } - } - Ref(ref inner_pat, _) | - Box(ref inner_pat) => { - pats.push_back(inner_pat); - } - TupleStruct(_, ref inner_pats, _) | - Tuple(ref inner_pats, _) => { - pats.extend(inner_pats.iter()); - } - Slice(ref pre_pats, ref inner_pat, ref post_pats) => { - pats.extend(pre_pats.iter()); - pats.extend(inner_pat.iter()); - pats.extend(post_pats.iter()); - } - _ => {} - } - } - - pat.each_binding(|_bm, hir_id, _sp, ident| { - ir.add_live_node_for_node(hir_id, VarDefNode(ident.span)); - ir.add_variable(Local(LocalInfo { - id: hir_id, - name: ident.name, - is_shorthand: shorthand_field_ids.contains(&hir_id) - })); - }); -} - -fn visit_local<'tcx>(ir: &mut IrMaps<'tcx>, local: &'tcx hir::Local) { - add_from_pat(ir, &local.pat); - intravisit::walk_local(ir, local); -} - -fn visit_arm<'tcx>(ir: &mut IrMaps<'tcx>, arm: &'tcx hir::Arm) { - for pat in &arm.pats { - add_from_pat(ir, pat); - } - intravisit::walk_arm(ir, arm); -} - -fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr) { - match expr.node { - // live nodes required for uses or definitions of variables: - hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => { - debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res); - if let Res::Local(var_hir_id) = path.res { - let upvars = ir.tcx.upvars(ir.body_owner); - if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hir_id)) { - ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); - } - } - intravisit::walk_expr(ir, expr); - } - hir::ExprKind::Closure(..) => { - // Interesting control flow (for loops can contain labeled - // breaks or continues) - ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); - - // Make a live_node for each captured variable, with the span - // being the location that the variable is used. This results - // in better error messages than just pointing at the closure - // construction site. - let mut call_caps = Vec::new(); - let closure_def_id = ir.tcx.hir().local_def_id(expr.hir_id); - if let Some(upvars) = ir.tcx.upvars(closure_def_id) { - let parent_upvars = ir.tcx.upvars(ir.body_owner); - call_caps.extend(upvars.iter().filter_map(|(&var_id, upvar)| { - let has_parent = parent_upvars - .map_or(false, |upvars| upvars.contains_key(&var_id)); - if !has_parent { - let upvar_ln = ir.add_live_node(UpvarNode(upvar.span)); - Some(CaptureInfo { ln: upvar_ln, var_hid: var_id }) - } else { - None - } - })); - } - ir.set_captures(expr.hir_id, call_caps); - let old_body_owner = ir.body_owner; - ir.body_owner = closure_def_id; - intravisit::walk_expr(ir, expr); - ir.body_owner = old_body_owner; - } - - // live nodes required for interesting control flow: - hir::ExprKind::Match(..) | - hir::ExprKind::Loop(..) => { - ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); - intravisit::walk_expr(ir, expr); - } - hir::ExprKind::Binary(op, ..) if op.node.is_lazy() => { - ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); - intravisit::walk_expr(ir, expr); - } - - // otherwise, live nodes are not required: - hir::ExprKind::Index(..) | - hir::ExprKind::Field(..) | - hir::ExprKind::Array(..) | - hir::ExprKind::Call(..) | - hir::ExprKind::MethodCall(..) | - hir::ExprKind::Tup(..) | - hir::ExprKind::Binary(..) | - hir::ExprKind::AddrOf(..) | - hir::ExprKind::Cast(..) | - hir::ExprKind::DropTemps(..) | - hir::ExprKind::Unary(..) | - hir::ExprKind::Break(..) | - hir::ExprKind::Continue(_) | - hir::ExprKind::Lit(_) | - hir::ExprKind::Ret(..) | - hir::ExprKind::Block(..) | - hir::ExprKind::Assign(..) | - hir::ExprKind::AssignOp(..) | - hir::ExprKind::Struct(..) | - hir::ExprKind::Repeat(..) | - hir::ExprKind::InlineAsm(..) | - hir::ExprKind::Box(..) | - hir::ExprKind::Yield(..) | - hir::ExprKind::Type(..) | - hir::ExprKind::Err | - hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => { - intravisit::walk_expr(ir, expr); - } - } -} - -// ______________________________________________________________________ -// Computing liveness sets -// -// Actually we compute just a bit more than just liveness, but we use -// the same basic propagation framework in all cases. - -#[derive(Clone, Copy)] -struct RWU { - reader: LiveNode, - writer: LiveNode, - used: bool -} - -/// Conceptually, this is like a `Vec<RWU>`. But the number of `RWU`s can get -/// very large, so it uses a more compact representation that takes advantage -/// of the fact that when the number of `RWU`s is large, most of them have an -/// invalid reader and an invalid writer. -struct RWUTable { - /// Each entry in `packed_rwus` is either INV_INV_FALSE, INV_INV_TRUE, or - /// an index into `unpacked_rwus`. In the common cases, this compacts the - /// 65 bits of data into 32; in the uncommon cases, it expands the 65 bits - /// in 96. - /// - /// More compact representations are possible -- e.g., use only 2 bits per - /// packed `RWU` and make the secondary table a HashMap that maps from - /// indices to `RWU`s -- but this one strikes a good balance between size - /// and speed. - packed_rwus: Vec<u32>, - unpacked_rwus: Vec<RWU>, -} - -// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: false }`. -const INV_INV_FALSE: u32 = u32::MAX; - -// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: true }`. -const INV_INV_TRUE: u32 = u32::MAX - 1; - -impl RWUTable { - fn new(num_rwus: usize) -> RWUTable { - Self { - packed_rwus: vec![INV_INV_FALSE; num_rwus], - unpacked_rwus: vec![], - } - } - - fn get(&self, idx: usize) -> RWU { - let packed_rwu = self.packed_rwus[idx]; - match packed_rwu { - INV_INV_FALSE => RWU { reader: invalid_node(), writer: invalid_node(), used: false }, - INV_INV_TRUE => RWU { reader: invalid_node(), writer: invalid_node(), used: true }, - _ => self.unpacked_rwus[packed_rwu as usize], - } - } - - fn get_reader(&self, idx: usize) -> LiveNode { - let packed_rwu = self.packed_rwus[idx]; - match packed_rwu { - INV_INV_FALSE | INV_INV_TRUE => invalid_node(), - _ => self.unpacked_rwus[packed_rwu as usize].reader, - } - } - - fn get_writer(&self, idx: usize) -> LiveNode { - let packed_rwu = self.packed_rwus[idx]; - match packed_rwu { - INV_INV_FALSE | INV_INV_TRUE => invalid_node(), - _ => self.unpacked_rwus[packed_rwu as usize].writer, - } - } - - fn get_used(&self, idx: usize) -> bool { - let packed_rwu = self.packed_rwus[idx]; - match packed_rwu { - INV_INV_FALSE => false, - INV_INV_TRUE => true, - _ => self.unpacked_rwus[packed_rwu as usize].used, - } - } - - #[inline] - fn copy_packed(&mut self, dst_idx: usize, src_idx: usize) { - self.packed_rwus[dst_idx] = self.packed_rwus[src_idx]; - } - - fn assign_unpacked(&mut self, idx: usize, rwu: RWU) { - if rwu.reader == invalid_node() && rwu.writer == invalid_node() { - // When we overwrite an indexing entry in `self.packed_rwus` with - // `INV_INV_{TRUE,FALSE}` we don't remove the corresponding entry - // from `self.unpacked_rwus`; it's not worth the effort, and we - // can't have entries shifting around anyway. - self.packed_rwus[idx] = if rwu.used { - INV_INV_TRUE - } else { - INV_INV_FALSE - } - } else { - // Add a new RWU to `unpacked_rwus` and make `packed_rwus[idx]` - // point to it. - self.packed_rwus[idx] = self.unpacked_rwus.len() as u32; - self.unpacked_rwus.push(rwu); - } - } - - fn assign_inv_inv(&mut self, idx: usize) { - self.packed_rwus[idx] = if self.get_used(idx) { - INV_INV_TRUE - } else { - INV_INV_FALSE - }; - } -} - -#[derive(Copy, Clone)] -struct Specials { - exit_ln: LiveNode, - fallthrough_ln: LiveNode, - clean_exit_var: Variable -} - -const ACC_READ: u32 = 1; -const ACC_WRITE: u32 = 2; -const ACC_USE: u32 = 4; - -struct Liveness<'a, 'tcx> { - ir: &'a mut IrMaps<'tcx>, - tables: &'a ty::TypeckTables<'tcx>, - s: Specials, - successors: Vec<LiveNode>, - rwu_table: RWUTable, - - // mappings from loop node ID to LiveNode - // ("break" label should map to loop node ID, - // it probably doesn't now) - break_ln: HirIdMap<LiveNode>, - cont_ln: HirIdMap<LiveNode>, -} - -impl<'a, 'tcx> Liveness<'a, 'tcx> { - fn new(ir: &'a mut IrMaps<'tcx>, body: hir::BodyId) -> Liveness<'a, 'tcx> { - // Special nodes and variables: - // - exit_ln represents the end of the fn, either by return or panic - // - implicit_ret_var is a pseudo-variable that represents - // an implicit return - let specials = Specials { - exit_ln: ir.add_live_node(ExitNode), - fallthrough_ln: ir.add_live_node(ExitNode), - clean_exit_var: ir.add_variable(CleanExit) - }; - - let tables = ir.tcx.body_tables(body); - - let num_live_nodes = ir.num_live_nodes; - let num_vars = ir.num_vars; - - Liveness { - ir, - tables, - s: specials, - successors: vec![invalid_node(); num_live_nodes], - rwu_table: RWUTable::new(num_live_nodes * num_vars), - break_ln: Default::default(), - cont_ln: Default::default(), - } - } - - fn live_node(&self, hir_id: HirId, span: Span) -> LiveNode { - match self.ir.live_node_map.get(&hir_id) { - Some(&ln) => ln, - None => { - // This must be a mismatch between the ir_map construction - // above and the propagation code below; the two sets of - // code have to agree about which AST nodes are worth - // creating liveness nodes for. - span_bug!( - span, - "no live node registered for node {:?}", - hir_id); - } - } - } - - fn variable(&self, hir_id: HirId, span: Span) -> Variable { - self.ir.variable(hir_id, span) - } - - fn pat_bindings<F>(&mut self, pat: &hir::Pat, mut f: F) where - F: FnMut(&mut Liveness<'a, 'tcx>, LiveNode, Variable, Span, HirId), - { - pat.each_binding(|_bm, hir_id, sp, n| { - let ln = self.live_node(hir_id, sp); - let var = self.variable(hir_id, n.span); - f(self, ln, var, n.span, hir_id); - }) - } - - fn arm_pats_bindings<F>(&mut self, pat: Option<&hir::Pat>, f: F) where - F: FnMut(&mut Liveness<'a, 'tcx>, LiveNode, Variable, Span, HirId), - { - if let Some(pat) = pat { - self.pat_bindings(pat, f); - } - } - - fn define_bindings_in_pat(&mut self, pat: &hir::Pat, succ: LiveNode) - -> LiveNode { - self.define_bindings_in_arm_pats(Some(pat), succ) - } - - fn define_bindings_in_arm_pats(&mut self, pat: Option<&hir::Pat>, succ: LiveNode) - -> LiveNode { - let mut succ = succ; - self.arm_pats_bindings(pat, |this, ln, var, _sp, _id| { - this.init_from_succ(ln, succ); - this.define(ln, var); - succ = ln; - }); - succ - } - - fn idx(&self, ln: LiveNode, var: Variable) -> usize { - ln.get() * self.ir.num_vars + var.get() - } - - fn live_on_entry(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> { - assert!(ln.is_valid()); - let reader = self.rwu_table.get_reader(self.idx(ln, var)); - if reader.is_valid() { Some(self.ir.lnk(reader)) } else { None } - } - - // Is this variable live on entry to any of its successor nodes? - fn live_on_exit(&self, ln: LiveNode, var: Variable) - -> Option<LiveNodeKind> { - let successor = self.successors[ln.get()]; - self.live_on_entry(successor, var) - } - - fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool { - assert!(ln.is_valid()); - self.rwu_table.get_used(self.idx(ln, var)) - } - - fn assigned_on_entry(&self, ln: LiveNode, var: Variable) - -> Option<LiveNodeKind> { - assert!(ln.is_valid()); - let writer = self.rwu_table.get_writer(self.idx(ln, var)); - if writer.is_valid() { Some(self.ir.lnk(writer)) } else { None } - } - - fn assigned_on_exit(&self, ln: LiveNode, var: Variable) - -> Option<LiveNodeKind> { - let successor = self.successors[ln.get()]; - self.assigned_on_entry(successor, var) - } - - fn indices2<F>(&mut self, ln: LiveNode, succ_ln: LiveNode, mut op: F) where - F: FnMut(&mut Liveness<'a, 'tcx>, usize, usize), - { - let node_base_idx = self.idx(ln, Variable(0)); - let succ_base_idx = self.idx(succ_ln, Variable(0)); - for var_idx in 0..self.ir.num_vars { - op(self, node_base_idx + var_idx, succ_base_idx + var_idx); - } - } - - fn write_vars<F>(&self, - wr: &mut dyn Write, - ln: LiveNode, - mut test: F) - -> io::Result<()> where - F: FnMut(usize) -> LiveNode, - { - let node_base_idx = self.idx(ln, Variable(0)); - for var_idx in 0..self.ir.num_vars { - let idx = node_base_idx + var_idx; - if test(idx).is_valid() { - write!(wr, " {:?}", Variable(var_idx as u32))?; - } - } - Ok(()) - } - - - #[allow(unused_must_use)] - fn ln_str(&self, ln: LiveNode) -> String { - let mut wr = Vec::new(); - { - let wr = &mut wr as &mut dyn Write; - write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln)); - self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx)); - write!(wr, " writes"); - self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx)); - write!(wr, " precedes {:?}]", self.successors[ln.get()]); - } - String::from_utf8(wr).unwrap() - } - - fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) { - self.successors[ln.get()] = succ_ln; - - // It is not necessary to initialize the RWUs here because they are all - // set to INV_INV_FALSE when they are created, and the sets only grow - // during iterations. - } - - fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) { - // more efficient version of init_empty() / merge_from_succ() - self.successors[ln.get()] = succ_ln; - - self.indices2(ln, succ_ln, |this, idx, succ_idx| { - this.rwu_table.copy_packed(idx, succ_idx); - }); - debug!("init_from_succ(ln={}, succ={})", - self.ln_str(ln), self.ln_str(succ_ln)); - } - - fn merge_from_succ(&mut self, - ln: LiveNode, - succ_ln: LiveNode, - first_merge: bool) - -> bool { - if ln == succ_ln { return false; } - - let mut changed = false; - self.indices2(ln, succ_ln, |this, idx, succ_idx| { - let mut rwu = this.rwu_table.get(idx); - let succ_rwu = this.rwu_table.get(succ_idx); - if succ_rwu.reader.is_valid() && !rwu.reader.is_valid() { - rwu.reader = succ_rwu.reader; - changed = true - } - - if succ_rwu.writer.is_valid() && !rwu.writer.is_valid() { - rwu.writer = succ_rwu.writer; - changed = true - } - - if succ_rwu.used && !rwu.used { - rwu.used = true; - changed = true; - } - - if changed { - this.rwu_table.assign_unpacked(idx, rwu); - } - }); - - debug!("merge_from_succ(ln={:?}, succ={}, first_merge={}, changed={})", - ln, self.ln_str(succ_ln), first_merge, changed); - return changed; - } - - // Indicates that a local variable was *defined*; we know that no - // uses of the variable can precede the definition (resolve checks - // this) so we just clear out all the data. - fn define(&mut self, writer: LiveNode, var: Variable) { - let idx = self.idx(writer, var); - self.rwu_table.assign_inv_inv(idx); - - debug!("{:?} defines {:?} (idx={}): {}", writer, var, - idx, self.ln_str(writer)); - } - - // Either read, write, or both depending on the acc bitset - fn acc(&mut self, ln: LiveNode, var: Variable, acc: u32) { - debug!("{:?} accesses[{:x}] {:?}: {}", - ln, acc, var, self.ln_str(ln)); - - let idx = self.idx(ln, var); - let mut rwu = self.rwu_table.get(idx); - - if (acc & ACC_WRITE) != 0 { - rwu.reader = invalid_node(); - rwu.writer = ln; - } - - // Important: if we both read/write, must do read second - // or else the write will override. - if (acc & ACC_READ) != 0 { - rwu.reader = ln; - } - - if (acc & ACC_USE) != 0 { - rwu.used = true; - } - - self.rwu_table.assign_unpacked(idx, rwu); - } - - fn compute(&mut self, body: &hir::Expr) -> LiveNode { - debug!("compute: using id for body, {}", - self.ir.tcx.hir().hir_to_pretty_string(body.hir_id)); - - // the fallthrough exit is only for those cases where we do not - // explicitly return: - let s = self.s; - self.init_from_succ(s.fallthrough_ln, s.exit_ln); - self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ); - - let entry_ln = self.propagate_through_expr(body, s.fallthrough_ln); - - // hack to skip the loop unless debug! is enabled: - debug!("^^ liveness computation results for body {} (entry={:?})", { - for ln_idx in 0..self.ir.num_live_nodes { - debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32))); - } - body.hir_id - }, - entry_ln); - - entry_ln - } - - fn propagate_through_block(&mut self, blk: &hir::Block, succ: LiveNode) - -> LiveNode { - if blk.targeted_by_break { - self.break_ln.insert(blk.hir_id, succ); - } - let succ = self.propagate_through_opt_expr(blk.expr.as_ref().map(|e| &**e), succ); - blk.stmts.iter().rev().fold(succ, |succ, stmt| { - self.propagate_through_stmt(stmt, succ) - }) - } - - fn propagate_through_stmt(&mut self, stmt: &hir::Stmt, succ: LiveNode) - -> LiveNode { - match stmt.node { - hir::StmtKind::Local(ref local) => { - // Note: we mark the variable as defined regardless of whether - // there is an initializer. Initially I had thought to only mark - // the live variable as defined if it was initialized, and then we - // could check for uninit variables just by scanning what is live - // at the start of the function. But that doesn't work so well for - // immutable variables defined in a loop: - // loop { let x; x = 5; } - // because the "assignment" loops back around and generates an error. - // - // So now we just check that variables defined w/o an - // initializer are not live at the point of their - // initialization, which is mildly more complex than checking - // once at the func header but otherwise equivalent. - - let succ = self.propagate_through_opt_expr(local.init.as_ref().map(|e| &**e), succ); - self.define_bindings_in_pat(&local.pat, succ) - } - hir::StmtKind::Item(..) => succ, - hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { - self.propagate_through_expr(&expr, succ) - } - } - } - - fn propagate_through_exprs(&mut self, exprs: &[Expr], succ: LiveNode) - -> LiveNode { - exprs.iter().rev().fold(succ, |succ, expr| { - self.propagate_through_expr(&expr, succ) - }) - } - - fn propagate_through_opt_expr(&mut self, - opt_expr: Option<&Expr>, - succ: LiveNode) - -> LiveNode { - opt_expr.map_or(succ, |expr| self.propagate_through_expr(expr, succ)) - } - - fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode) - -> LiveNode { - debug!("propagate_through_expr: {}", self.ir.tcx.hir().hir_to_pretty_string(expr.hir_id)); - - match expr.node { - // Interesting cases with control flow or which gen/kill - hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => { - self.access_path(expr.hir_id, path, succ, ACC_READ | ACC_USE) - } - - hir::ExprKind::Field(ref e, _) => { - self.propagate_through_expr(&e, succ) - } - - hir::ExprKind::Closure(..) => { - debug!("{} is an ExprKind::Closure", - self.ir.tcx.hir().hir_to_pretty_string(expr.hir_id)); - - // the construction of a closure itself is not important, - // but we have to consider the closed over variables. - let caps = self.ir.capture_info_map.get(&expr.hir_id).cloned().unwrap_or_else(|| - span_bug!(expr.span, "no registered caps")); - - caps.iter().rev().fold(succ, |succ, cap| { - self.init_from_succ(cap.ln, succ); - let var = self.variable(cap.var_hid, expr.span); - self.acc(cap.ln, var, ACC_READ | ACC_USE); - cap.ln - }) - } - - // Note that labels have been resolved, so we don't need to look - // at the label ident - hir::ExprKind::Loop(ref blk, _, _) => { - self.propagate_through_loop(expr, &blk, succ) - } - - hir::ExprKind::Match(ref e, ref arms, _) => { - // - // (e) - // | - // v - // (expr) - // / | \ - // | | | - // v v v - // (..arms..) - // | | | - // v v v - // ( succ ) - // - // - let ln = self.live_node(expr.hir_id, expr.span); - self.init_empty(ln, succ); - let mut first_merge = true; - for arm in arms { - let body_succ = self.propagate_through_expr(&arm.body, succ); - - let guard_succ = self.propagate_through_opt_expr( - arm.guard.as_ref().map(|hir::Guard::If(e)| &**e), - body_succ - ); - // only consider the first pattern; any later patterns must have - // the same bindings, and we also consider the first pattern to be - // the "authoritative" set of ids - let arm_succ = - self.define_bindings_in_arm_pats(arm.pats.first().map(|p| &**p), - guard_succ); - self.merge_from_succ(ln, arm_succ, first_merge); - first_merge = false; - }; - self.propagate_through_expr(&e, ln) - } - - hir::ExprKind::Ret(ref o_e) => { - // ignore succ and subst exit_ln: - let exit_ln = self.s.exit_ln; - self.propagate_through_opt_expr(o_e.as_ref().map(|e| &**e), exit_ln) - } - - hir::ExprKind::Break(label, ref opt_expr) => { - // Find which label this break jumps to - let target = match label.target_id { - Ok(hir_id) => self.break_ln.get(&hir_id), - Err(err) => span_bug!(expr.span, "loop scope error: {}", err), - }.cloned(); - - // Now that we know the label we're going to, - // look it up in the break loop nodes table - - match target { - Some(b) => self.propagate_through_opt_expr(opt_expr.as_ref().map(|e| &**e), b), - None => span_bug!(expr.span, "break to unknown label") - } - } - - hir::ExprKind::Continue(label) => { - // Find which label this expr continues to - let sc = label.target_id.unwrap_or_else(|err| - span_bug!(expr.span, "loop scope error: {}", err)); - - // Now that we know the label we're going to, - // look it up in the continue loop nodes table - self.cont_ln.get(&sc).cloned().unwrap_or_else(|| - span_bug!(expr.span, "continue to unknown label")) - } - - hir::ExprKind::Assign(ref l, ref r) => { - // see comment on places in - // propagate_through_place_components() - let succ = self.write_place(&l, succ, ACC_WRITE); - let succ = self.propagate_through_place_components(&l, succ); - self.propagate_through_expr(&r, succ) - } - - hir::ExprKind::AssignOp(_, ref l, ref r) => { - // an overloaded assign op is like a method call - if self.tables.is_method_call(expr) { - let succ = self.propagate_through_expr(&l, succ); - self.propagate_through_expr(&r, succ) - } else { - // see comment on places in - // propagate_through_place_components() - let succ = self.write_place(&l, succ, ACC_WRITE|ACC_READ); - let succ = self.propagate_through_expr(&r, succ); - self.propagate_through_place_components(&l, succ) - } - } - - // Uninteresting cases: just propagate in rev exec order - - hir::ExprKind::Array(ref exprs) => { - self.propagate_through_exprs(exprs, succ) - } - - hir::ExprKind::Struct(_, ref fields, ref with_expr) => { - let succ = self.propagate_through_opt_expr(with_expr.as_ref().map(|e| &**e), succ); - fields.iter().rev().fold(succ, |succ, field| { - self.propagate_through_expr(&field.expr, succ) - }) - } - - hir::ExprKind::Call(ref f, ref args) => { - let m = self.ir.tcx.hir().get_module_parent(expr.hir_id); - let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) { - self.s.exit_ln - } else { - succ - }; - let succ = self.propagate_through_exprs(args, succ); - self.propagate_through_expr(&f, succ) - } - - hir::ExprKind::MethodCall(.., ref args) => { - let m = self.ir.tcx.hir().get_module_parent(expr.hir_id); - let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) { - self.s.exit_ln - } else { - succ - }; - - self.propagate_through_exprs(args, succ) - } - - hir::ExprKind::Tup(ref exprs) => { - self.propagate_through_exprs(exprs, succ) - } - - hir::ExprKind::Binary(op, ref l, ref r) if op.node.is_lazy() => { - let r_succ = self.propagate_through_expr(&r, succ); - - let ln = self.live_node(expr.hir_id, expr.span); - self.init_from_succ(ln, succ); - self.merge_from_succ(ln, r_succ, false); - - self.propagate_through_expr(&l, ln) - } - - hir::ExprKind::Index(ref l, ref r) | - hir::ExprKind::Binary(_, ref l, ref r) => { - let r_succ = self.propagate_through_expr(&r, succ); - self.propagate_through_expr(&l, r_succ) - } - - hir::ExprKind::Box(ref e) | - hir::ExprKind::AddrOf(_, ref e) | - hir::ExprKind::Cast(ref e, _) | - hir::ExprKind::Type(ref e, _) | - hir::ExprKind::DropTemps(ref e) | - hir::ExprKind::Unary(_, ref e) | - hir::ExprKind::Yield(ref e, _) | - hir::ExprKind::Repeat(ref e, _) => { - self.propagate_through_expr(&e, succ) - } - - hir::ExprKind::InlineAsm(ref ia, ref outputs, ref inputs) => { - let succ = ia.outputs.iter().zip(outputs).rev().fold(succ, |succ, (o, output)| { - // see comment on places - // in propagate_through_place_components() - if o.is_indirect { - self.propagate_through_expr(output, succ) - } else { - let acc = if o.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE }; - let succ = self.write_place(output, succ, acc); - self.propagate_through_place_components(output, succ) - }}); - - // Inputs are executed first. Propagate last because of rev order - self.propagate_through_exprs(inputs, succ) - } - - hir::ExprKind::Lit(..) | hir::ExprKind::Err | - hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => { - succ - } - - // Note that labels have been resolved, so we don't need to look - // at the label ident - hir::ExprKind::Block(ref blk, _) => { - self.propagate_through_block(&blk, succ) - } - } - } - - fn propagate_through_place_components(&mut self, - expr: &Expr, - succ: LiveNode) - -> LiveNode { - // # Places - // - // In general, the full flow graph structure for an - // assignment/move/etc can be handled in one of two ways, - // depending on whether what is being assigned is a "tracked - // value" or not. A tracked value is basically a local - // variable or argument. - // - // The two kinds of graphs are: - // - // Tracked place Untracked place - // ----------------------++----------------------- - // || - // | || | - // v || v - // (rvalue) || (rvalue) - // | || | - // v || v - // (write of place) || (place components) - // | || | - // v || v - // (succ) || (succ) - // || - // ----------------------++----------------------- - // - // I will cover the two cases in turn: - // - // # Tracked places - // - // A tracked place is a local variable/argument `x`. In - // these cases, the link_node where the write occurs is linked - // to node id of `x`. The `write_place()` routine generates - // the contents of this node. There are no subcomponents to - // consider. - // - // # Non-tracked places - // - // These are places like `x[5]` or `x.f`. In that case, we - // basically ignore the value which is written to but generate - // reads for the components---`x` in these two examples. The - // components reads are generated by - // `propagate_through_place_components()` (this fn). - // - // # Illegal places - // - // It is still possible to observe assignments to non-places; - // these errors are detected in the later pass borrowck. We - // just ignore such cases and treat them as reads. - - match expr.node { - hir::ExprKind::Path(_) => succ, - hir::ExprKind::Field(ref e, _) => self.propagate_through_expr(&e, succ), - _ => self.propagate_through_expr(expr, succ) - } - } - - // see comment on propagate_through_place() - fn write_place(&mut self, expr: &Expr, succ: LiveNode, acc: u32) -> LiveNode { - match expr.node { - hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => { - self.access_path(expr.hir_id, path, succ, acc) - } - - // We do not track other places, so just propagate through - // to their subcomponents. Also, it may happen that - // non-places occur here, because those are detected in the - // later pass borrowck. - _ => succ - } - } - - fn access_var(&mut self, hir_id: HirId, var_hid: HirId, succ: LiveNode, acc: u32, span: Span) - -> LiveNode { - let ln = self.live_node(hir_id, span); - if acc != 0 { - self.init_from_succ(ln, succ); - let var = self.variable(var_hid, span); - self.acc(ln, var, acc); - } - ln - } - - fn access_path(&mut self, hir_id: HirId, path: &hir::Path, succ: LiveNode, acc: u32) - -> LiveNode { - match path.res { - Res::Local(hid) => { - let upvars = self.ir.tcx.upvars(self.ir.body_owner); - if !upvars.map_or(false, |upvars| upvars.contains_key(&hid)) { - self.access_var(hir_id, hid, succ, acc, path.span) - } else { - succ - } - } - _ => succ - } - } - - fn propagate_through_loop( - &mut self, - expr: &Expr, - body: &hir::Block, - succ: LiveNode - ) -> LiveNode { - /* - We model control flow like this: - - (expr) <-+ - | | - v | - (body) --+ - - Note that a `continue` expression targeting the `loop` will have a successor of `expr`. - Meanwhile, a `break` expression will have a successor of `succ`. - */ - - // first iteration: - let mut first_merge = true; - let ln = self.live_node(expr.hir_id, expr.span); - self.init_empty(ln, succ); - debug!("propagate_through_loop: using id for loop body {} {}", - expr.hir_id, self.ir.tcx.hir().hir_to_pretty_string(body.hir_id)); - - self.break_ln.insert(expr.hir_id, succ); - - self.cont_ln.insert(expr.hir_id, ln); - - let body_ln = self.propagate_through_block(body, ln); - - // repeat until fixed point is reached: - while self.merge_from_succ(ln, body_ln, first_merge) { - first_merge = false; - assert_eq!(body_ln, self.propagate_through_block(body, ln)); - } - - ln - } -} - -// _______________________________________________________________________ -// Checking for error conditions - -impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::None - } - - fn visit_local(&mut self, l: &'tcx hir::Local) { - check_local(self, l); - } - fn visit_expr(&mut self, ex: &'tcx Expr) { - check_expr(self, ex); - } - fn visit_arm(&mut self, a: &'tcx hir::Arm) { - check_arm(self, a); - } -} - -fn check_local<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, local: &'tcx hir::Local) { - match local.init { - Some(_) => { - this.warn_about_unused_or_dead_vars_in_pat(&local.pat); - }, - None => { - this.pat_bindings(&local.pat, |this, ln, var, sp, id| { - let span = local.pat.simple_ident().map_or(sp, |ident| ident.span); - this.warn_about_unused(vec![span], id, ln, var); - }) - } - } - - intravisit::walk_local(this, local); -} - -fn check_arm<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, arm: &'tcx hir::Arm) { - // Only consider the variable from the first pattern; any later patterns must have - // the same bindings, and we also consider the first pattern to be the "authoritative" set of - // ids. However, we should take the spans of variables with the same name from the later - // patterns so the suggestions to prefix with underscores will apply to those too. - let mut vars: BTreeMap<String, (LiveNode, Variable, HirId, Vec<Span>)> = Default::default(); - - for pat in &arm.pats { - this.arm_pats_bindings(Some(&*pat), |this, ln, var, sp, id| { - let name = this.ir.variable_name(var); - vars.entry(name) - .and_modify(|(.., spans)| { - spans.push(sp); - }) - .or_insert_with(|| { - (ln, var, id, vec![sp]) - }); - }); - } - - for (_, (ln, var, id, spans)) in vars { - this.warn_about_unused(spans, id, ln, var); - } - - intravisit::walk_arm(this, arm); -} - -fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) { - match expr.node { - hir::ExprKind::Assign(ref l, _) => { - this.check_place(&l); - - intravisit::walk_expr(this, expr); - } - - hir::ExprKind::AssignOp(_, ref l, _) => { - if !this.tables.is_method_call(expr) { - this.check_place(&l); - } - - intravisit::walk_expr(this, expr); - } - - hir::ExprKind::InlineAsm(ref ia, ref outputs, ref inputs) => { - for input in inputs { - this.visit_expr(input); - } - - // Output operands must be places - for (o, output) in ia.outputs.iter().zip(outputs) { - if !o.is_indirect { - this.check_place(output); - } - this.visit_expr(output); - } - - intravisit::walk_expr(this, expr); - } - - // no correctness conditions related to liveness - hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) | - hir::ExprKind::Match(..) | hir::ExprKind::Loop(..) | - hir::ExprKind::Index(..) | hir::ExprKind::Field(..) | - hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) | hir::ExprKind::Binary(..) | - hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) | hir::ExprKind::Unary(..) | - hir::ExprKind::Ret(..) | hir::ExprKind::Break(..) | hir::ExprKind::Continue(..) | - hir::ExprKind::Lit(_) | hir::ExprKind::Block(..) | hir::ExprKind::AddrOf(..) | - hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) | - hir::ExprKind::Closure(..) | hir::ExprKind::Path(_) | hir::ExprKind::Yield(..) | - hir::ExprKind::Box(..) | hir::ExprKind::Type(..) | hir::ExprKind::Err => { - intravisit::walk_expr(this, expr); - } - } -} - -impl<'a, 'tcx> Liveness<'a, 'tcx> { - fn check_place(&mut self, expr: &'tcx Expr) { - match expr.node { - hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => { - if let Res::Local(var_hid) = path.res { - let upvars = self.ir.tcx.upvars(self.ir.body_owner); - if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hid)) { - // Assignment to an immutable variable or argument: only legal - // if there is no later assignment. If this local is actually - // mutable, then check for a reassignment to flag the mutability - // as being used. - let ln = self.live_node(expr.hir_id, expr.span); - let var = self.variable(var_hid, expr.span); - self.warn_about_dead_assign(expr.span, expr.hir_id, ln, var); - } - } - } - _ => { - // For other kinds of places, no checks are required, - // and any embedded expressions are actually rvalues - intravisit::walk_expr(self, expr); - } - } - } - - fn should_warn(&self, var: Variable) -> Option<String> { - let name = self.ir.variable_name(var); - if name.is_empty() || name.as_bytes()[0] == b'_' { - None - } else { - Some(name) - } - } - - fn warn_about_unused_args(&self, body: &hir::Body, entry_ln: LiveNode) { - for param in &body.params { - param.pat.each_binding(|_bm, hir_id, _, ident| { - let sp = ident.span; - let var = self.variable(hir_id, sp); - // Ignore unused self. - if ident.name != kw::SelfLower { - if !self.warn_about_unused(vec![sp], hir_id, entry_ln, var) { - if self.live_on_entry(entry_ln, var).is_none() { - self.report_dead_assign(hir_id, sp, var, true); - } - } - } - }) - } - } - - fn warn_about_unused_or_dead_vars_in_pat(&mut self, pat: &hir::Pat) { - self.pat_bindings(pat, |this, ln, var, sp, id| { - if !this.warn_about_unused(vec![sp], id, ln, var) { - this.warn_about_dead_assign(sp, id, ln, var); - } - }) - } - - fn warn_about_unused(&self, - spans: Vec<Span>, - hir_id: HirId, - ln: LiveNode, - var: Variable) - -> bool { - if !self.used_on_entry(ln, var) { - let r = self.should_warn(var); - if let Some(name) = r { - // annoying: for parameters in funcs like `fn(x: i32) - // {ret}`, there is only one node, so asking about - // assigned_on_exit() is not meaningful. - let is_assigned = if ln == self.s.exit_ln { - false - } else { - self.assigned_on_exit(ln, var).is_some() - }; - - if is_assigned { - self.ir.tcx.lint_hir_note( - lint::builtin::UNUSED_VARIABLES, - hir_id, - spans, - &format!("variable `{}` is assigned to, but never used", name), - &format!("consider using `_{}` instead", name), - ); - } else if name != "self" { - let mut err = self.ir.tcx.struct_span_lint_hir( - lint::builtin::UNUSED_VARIABLES, - hir_id, - spans.clone(), - &format!("unused variable: `{}`", name), - ); - - if self.ir.variable_is_shorthand(var) { - if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) { - // Handle `ref` and `ref mut`. - let spans = spans.iter() - .map(|_span| (pat.span, format!("{}: _", name))) - .collect(); - - err.multipart_suggestion( - "try ignoring the field", - spans, - Applicability::MachineApplicable, - ); - } - } else { - err.multipart_suggestion( - "consider prefixing with an underscore", - spans.iter().map(|span| (*span, format!("_{}", name))).collect(), - Applicability::MachineApplicable, - ); - } - - err.emit() - } - } - true - } else { - false - } - } - - fn warn_about_dead_assign(&self, sp: Span, hir_id: HirId, ln: LiveNode, var: Variable) { - if self.live_on_exit(ln, var).is_none() { - self.report_dead_assign(hir_id, sp, var, false); - } - } - - fn report_dead_assign(&self, hir_id: HirId, sp: Span, var: Variable, is_argument: bool) { - if let Some(name) = self.should_warn(var) { - if is_argument { - self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, sp, - &format!("value passed to `{}` is never read", name)) - .help("maybe it is overwritten before being read?") - .emit(); - } else { - self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, sp, - &format!("value assigned to `{}` is never read", name)) - .help("maybe it is overwritten before being read?") - .emit(); - } - } - } -}
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 73ca981..cbf336f 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs
@@ -79,12 +79,11 @@ use std::hash::{Hash, Hasher}; use rustc_data_structures::fx::FxIndexMap; use std::rc::Rc; -use crate::util::nodemap::ItemLocalSet; #[derive(Clone, Debug, PartialEq)] pub enum Categorization<'tcx> { - Rvalue(ty::Region<'tcx>), // temporary val, argument is its scope - ThreadLocal(ty::Region<'tcx>), // value that cannot move, but still restricted in scope + Rvalue, // temporary val + ThreadLocal, // value that cannot move, but still restricted in scope StaticItem, Upvar(Upvar), // upvar referenced by closure env Local(hir::HirId), // local variable @@ -103,7 +102,7 @@ } // different kinds of pointers: -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum PointerKind<'tcx> { /// `Box<T>` Unique, @@ -117,7 +116,7 @@ // We use the term "interior" to mean "something reachable from the // base without a pointer dereference", e.g., a field -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, PartialEq)] pub enum InteriorKind { InteriorField(FieldIndex), InteriorElement(InteriorOffsetKind), @@ -140,13 +139,13 @@ } } -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, PartialEq)] pub enum InteriorOffsetKind { Index, // e.g., `array_expr[index_expr]` Pattern, // e.g., `fn foo([_, a, _, _]: [A; 4]) { ... }` } -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Debug)] pub enum MutabilityCategory { McImmutable, // Immutable. McDeclared, // Directly declared as mutable. @@ -219,7 +218,6 @@ pub upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, pub region_scope_tree: &'a region::ScopeTree, pub tables: &'a ty::TypeckTables<'tcx>, - rvalue_promotable_map: Option<&'tcx ItemLocalSet>, infcx: Option<&'a InferCtxt<'a, 'tcx>>, } @@ -271,7 +269,7 @@ id: hir::HirId, ) -> MutabilityCategory { let ret = match tcx.hir().get(id) { - Node::Binding(p) => match p.node { + Node::Binding(p) => match p.kind { PatKind::Binding(..) => { let bm = *tables.pat_binding_modes() .get(p.hir_id) @@ -335,7 +333,6 @@ body_owner: DefId, region_scope_tree: &'a region::ScopeTree, tables: &'a ty::TypeckTables<'tcx>, - rvalue_promotable_map: Option<&'tcx ItemLocalSet>, ) -> MemCategorizationContext<'a, 'tcx> { MemCategorizationContext { tcx, @@ -343,7 +340,6 @@ upvars: tcx.upvars(body_owner), region_scope_tree, tables, - rvalue_promotable_map, infcx: None, param_env, } @@ -369,19 +365,12 @@ ) -> MemCategorizationContext<'a, 'tcx> { let tcx = infcx.tcx; - // Subtle: we can't do rvalue promotion analysis until the - // typeck phase is complete, which means that you can't trust - // the rvalue lifetimes that result, but that's ok, since we - // don't need to know those during type inference. - let rvalue_promotable_map = None; - MemCategorizationContext { tcx, body_owner, upvars: tcx.upvars(body_owner), region_scope_tree, tables, - rvalue_promotable_map, infcx: Some(infcx), param_env, } @@ -486,7 +475,7 @@ // This code detects whether we are looking at a `ref x`, // and if so, figures out what the type *being borrowed* is. - let ret_ty = match pat.node { + let ret_ty = match pat.kind { PatKind::Binding(..) => { let bm = *self.tables .pat_binding_modes() @@ -577,7 +566,7 @@ debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr); let expr_ty = self.expr_ty(expr)?; - match expr.node { + match expr.kind { hir::ExprKind::Unary(hir::UnDeref, ref e_base) => { if self.tables.is_method_call(expr) { self.cat_overloaded_place(expr, e_base, NoteNone) @@ -664,8 +653,7 @@ .any(|attr| attr.check_name(sym::thread_local)); let cat = if is_thread_local { - let re = self.temporary_scope(hir_id.local_id); - Categorization::ThreadLocal(re) + Categorization::ThreadLocal } else { Categorization::StaticItem }; @@ -738,18 +726,20 @@ LocalDefId::from_def_id(closure_expr_def_id), ); let ty = self.node_ty(fn_hir_id)?; - let kind = match ty.sty { + let kind = match ty.kind { ty::Generator(..) => ty::ClosureKind::FnOnce, - ty::Closure(closure_def_id, closure_substs) => { + ty::Closure(closure_def_id, substs) => { match self.infcx { // During upvar inference we may not know the // closure kind, just use the LATTICE_BOTTOM value. Some(infcx) => - infcx.closure_kind(closure_def_id, closure_substs) - .unwrap_or(ty::ClosureKind::LATTICE_BOTTOM), + infcx.closure_kind( + closure_def_id, + substs + ).unwrap_or(ty::ClosureKind::LATTICE_BOTTOM), None => - closure_substs.closure_kind(closure_def_id, self.tcx.global_tcx()), + substs.as_closure().kind(closure_def_id, self.tcx), } } _ => span_bug!(span, "unexpected type for fn in mem_categorization: {:?}", ty), @@ -876,16 +866,6 @@ ret } - /// Returns the lifetime of a temporary created by expr with id `id`. - /// This could be `'static` if `id` is part of a constant expression. - pub fn temporary_scope(&self, id: hir::ItemLocalId) -> ty::Region<'tcx> { - let scope = self.region_scope_tree.temporary_scope(id); - self.tcx.mk_region(match scope { - Some(scope) => ty::ReScope(scope), - None => ty::ReStatic - }) - } - pub fn cat_rvalue_node(&self, hir_id: hir::HirId, span: Span, @@ -894,28 +874,7 @@ debug!("cat_rvalue_node(id={:?}, span={:?}, expr_ty={:?})", hir_id, span, expr_ty); - let promotable = self.rvalue_promotable_map.as_ref().map(|m| m.contains(&hir_id.local_id)) - .unwrap_or(false); - - debug!("cat_rvalue_node: promotable = {:?}", promotable); - - // Always promote `[T; 0]` (even when e.g., borrowed mutably). - let promotable = match expr_ty.sty { - ty::Array(_, len) if len.try_eval_usize(self.tcx, self.param_env) == Some(0) => true, - _ => promotable, - }; - - debug!("cat_rvalue_node: promotable = {:?} (2)", promotable); - - // Compute maximum lifetime of this rvalue. This is 'static if - // we can promote to a constant, otherwise equal to enclosing temp - // lifetime. - let re = if promotable { - self.tcx.lifetimes.re_static - } else { - self.temporary_scope(hir_id.local_id) - }; - let ret = self.cat_rvalue(hir_id, span, re, expr_ty); + let ret = self.cat_rvalue(hir_id, span, expr_ty); debug!("cat_rvalue_node ret {:?}", ret); ret } @@ -923,12 +882,11 @@ pub fn cat_rvalue(&self, cmt_hir_id: hir::HirId, span: Span, - temp_scope: ty::Region<'tcx>, expr_ty: Ty<'tcx>) -> cmt_<'tcx> { let ret = cmt_ { hir_id: cmt_hir_id, span:span, - cat:Categorization::Rvalue(temp_scope), + cat:Categorization::Rvalue, mutbl:McDeclared, ty:expr_ty, note: NoteNone @@ -974,7 +932,7 @@ let place_ty = self.expr_ty(expr)?; let base_ty = self.expr_ty_adjusted(base)?; - let (region, mutbl) = match base_ty.sty { + let (region, mutbl) = match base_ty.kind { ty::Ref(region, _, mutbl) => (region, mutbl), _ => span_bug!(expr.span, "cat_overloaded_place: base is not a reference") }; @@ -1004,7 +962,7 @@ } }; - let ptr = match base_cmt.ty.sty { + let ptr = match base_cmt.ty.kind { ty::Adt(def, ..) if def.is_box() => Unique, ty::RawPtr(ref mt) => UnsafePtr(mt.mutbl), ty::Ref(r, _, mutbl) => { @@ -1212,7 +1170,7 @@ // that (where the `ref` on `x` is implied). op(cmt.clone(), pat); - match pat.node { + match pat.kind { PatKind::TupleStruct(ref qpath, ref subpats, ddpos) => { let res = self.tables.qpath_res(qpath, pat.hir_id); let (cmt, expected_len) = match res { @@ -1230,7 +1188,7 @@ Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), _) | Res::SelfCtor(..) => { let ty = self.pat_ty_unadjusted(&pat)?; - match ty.sty { + match ty.kind { ty::Adt(adt_def, _) => { (cmt, adt_def.non_enum_variant().fields.len()) } @@ -1303,7 +1261,7 @@ PatKind::Tuple(ref subpats, ddpos) => { // (p1, ..., pN) let ty = self.pat_ty_unadjusted(&pat)?; - let expected_len = match ty.sty { + let expected_len = match ty.kind { ty::Tuple(ref tys) => tys.len(), _ => span_bug!(pat.span, "tuple pattern unexpected type {:?}", ty), }; @@ -1376,9 +1334,9 @@ //! determines how long the value in `self` remains live. match self.cat { - Categorization::Rvalue(..) | + Categorization::Rvalue | Categorization::StaticItem | - Categorization::ThreadLocal(..) | + Categorization::ThreadLocal | Categorization::Local(..) | Categorization::Deref(_, UnsafePtr(..)) | Categorization::Deref(_, BorrowedPtr(..)) | @@ -1409,8 +1367,8 @@ b.freely_aliasable() } - Categorization::Rvalue(..) | - Categorization::ThreadLocal(..) | + Categorization::Rvalue | + Categorization::ThreadLocal | Categorization::Local(..) | Categorization::Upvar(..) | Categorization::Deref(_, UnsafePtr(..)) => { // yes, it's aliasable, but... @@ -1457,10 +1415,10 @@ Categorization::StaticItem => { "static item".into() } - Categorization::ThreadLocal(..) => { + Categorization::ThreadLocal => { "thread-local static item".into() } - Categorization::Rvalue(..) => { + Categorization::Rvalue => { "non-place".into() } Categorization::Local(vid) => {
diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index c2bcd46..8be64bf 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs
@@ -32,7 +32,7 @@ return true } - match item.node { + match item.kind { hir::ItemKind::Fn(_, header, ..) if header.is_const() => { return true; } @@ -55,7 +55,7 @@ if codegen_fn_attrs.requests_inline() || generics.requires_monomorphization(tcx) { return true } - if let hir::ImplItemKind::Method(method_sig, _) = &impl_item.node { + if let hir::ImplItemKind::Method(method_sig, _) = &impl_item.kind { if method_sig.header.is_const() { return true } @@ -100,7 +100,7 @@ } fn visit_expr(&mut self, expr: &'tcx hir::Expr) { - let res = match expr.node { + let res = match expr.kind { hir::ExprKind::Path(ref qpath) => { Some(self.tables.qpath_res(qpath, expr.hir_id)) } @@ -157,14 +157,14 @@ match self.tcx.hir().find(hir_id) { Some(Node::Item(item)) => { - match item.node { + match item.kind { hir::ItemKind::Fn(..) => item_might_be_inlined(self.tcx, &item, self.tcx.codegen_fn_attrs(def_id)), _ => false, } } Some(Node::TraitItem(trait_method)) => { - match trait_method.node { + match trait_method.kind { hir::TraitItemKind::Const(_, ref default) => default.is_some(), hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => true, hir::TraitItemKind::Method(_, hir::TraitMethod::Required(_)) | @@ -172,7 +172,7 @@ } } Some(Node::ImplItem(impl_item)) => { - match impl_item.node { + match impl_item.kind { hir::ImplItemKind::Const(..) => true, hir::ImplItemKind::Method(..) => { let attrs = self.tcx.codegen_fn_attrs(def_id); @@ -187,7 +187,7 @@ // type of the impl require inlining, this method // does too. let impl_hir_id = self.tcx.hir().as_local_hir_id(impl_did).unwrap(); - match self.tcx.hir().expect_item(impl_hir_id).node { + match self.tcx.hir().expect_item(impl_hir_id).kind { hir::ItemKind::Impl(..) => { let generics = self.tcx.generics_of(impl_did); generics.requires_monomorphization(self.tcx) @@ -225,7 +225,7 @@ // If we are building an executable, only explicitly extern // types need to be exported. if let Node::Item(item) = *node { - let reachable = if let hir::ItemKind::Fn(_, header, ..) = item.node { + let reachable = if let hir::ItemKind::Fn(_, header, ..) = item.kind { header.abi != Abi::Rust } else { false @@ -249,7 +249,7 @@ match *node { Node::Item(item) => { - match item.node { + match item.kind { hir::ItemKind::Fn(.., body) => { let def_id = self.tcx.hir().local_def_id(item.hir_id); if item_might_be_inlined(self.tcx, @@ -286,7 +286,7 @@ } } Node::TraitItem(trait_method) => { - match trait_method.node { + match trait_method.kind { hir::TraitItemKind::Const(_, None) | hir::TraitItemKind::Method(_, hir::TraitMethod::Required(_)) => { // Keep going, nothing to get exported @@ -299,7 +299,7 @@ } } Node::ImplItem(impl_item) => { - match impl_item.node { + match impl_item.kind { hir::ImplItemKind::Const(_, body) => { self.visit_nested_body(body); } @@ -313,7 +313,7 @@ hir::ImplItemKind::TyAlias(_) => {} } } - Node::Expr(&hir::Expr { node: hir::ExprKind::Closure(.., body, _, _), .. }) => { + Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(.., body, _, _), .. }) => { self.visit_nested_body(body); } // Nothing to recurse on for these @@ -361,7 +361,7 @@ } // We need only trait impls here, not inherent impls, and only non-exported ones - if let hir::ItemKind::Impl(.., Some(ref trait_ref), _, ref impl_item_refs) = item.node { + if let hir::ItemKind::Impl(.., Some(ref trait_ref), _, ref impl_item_refs) = item.kind { if !self.access_levels.is_reachable(item.hir_id) { self.worklist.extend(impl_item_refs.iter().map(|ii_ref| ii_ref.id.hir_id));
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 8747014..9ff2052 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs
@@ -16,8 +16,8 @@ use crate::ty::{self, DefIdTree, TyCtxt}; use crate::ty::query::Providers; -use rustc_data_structures::indexed_vec::Idx; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_index::vec::Idx; use rustc_macros::HashStable; use syntax::source_map; use syntax_pos::{Span, DUMMY_SP}; @@ -131,7 +131,7 @@ Remainder(FirstStatementIndex) } -newtype_index! { +rustc_index::newtype_index! { /// Represents a subscope of `block` for a binding that is introduced /// by `block.stmts[first_statement_index]`. Such subscopes represent /// a suffix of the block. Note that each subscope does not include @@ -796,7 +796,7 @@ // index information.) for (i, statement) in blk.stmts.iter().enumerate() { - match statement.node { + match statement.kind { hir::StmtKind::Local(..) | hir::StmtKind::Item(..) => { // Each declaration introduces a subscope for bindings @@ -850,7 +850,7 @@ visitor.record_child_scope(Scope { id: pat.hir_id.local_id, data: ScopeData::Node }); // If this is a binding then record the lifetime of that binding. - if let PatKind::Binding(..) = pat.node { + if let PatKind::Binding(..) = pat.kind { record_var_lifetime(visitor, pat.hir_id.local_id, pat.span); } @@ -893,7 +893,7 @@ let mut terminating = |id: hir::ItemLocalId| { terminating_scopes.insert(id); }; - match expr.node { + match expr.kind { // Conditional or repeating scopes are always terminating // scopes, meaning that temporaries cannot outlive them. // This ensures fixed size stacks. @@ -996,7 +996,7 @@ // properly, we can't miss any types. - match expr.node { + match expr.kind { // Manually recurse over closures, because they are the only // case of nested bodies that share the parent environment. hir::ExprKind::Closure(.., body, _, _) => { @@ -1053,7 +1053,7 @@ debug!("resolve_expr post-increment {}, expr = {:?}", visitor.expr_and_pat_count, expr); - if let hir::ExprKind::Yield(_, source) = &expr.node { + if let hir::ExprKind::Yield(_, source) = &expr.kind { // Mark this expr's scope and all parent scopes as containing `yield`. let mut scope = Scope { id: expr.hir_id.local_id, data: ScopeData::Node }; loop { @@ -1198,7 +1198,7 @@ // In the former case (the implicit ref version), the temporary is created by the // & expression, and its lifetime would be extended to the end of the block (due // to a different rule, not the below code). - match pat.node { + match pat.kind { PatKind::Binding(hir::BindingAnnotation::Ref, ..) | PatKind::Binding(hir::BindingAnnotation::RefMut, ..) => true, @@ -1240,7 +1240,7 @@ expr: &hir::Expr, blk_id: Option<Scope>, ) { - match expr.node { + match expr.kind { hir::ExprKind::AddrOf(_, ref subexpr) => { record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id); record_rvalue_scope(visitor, &subexpr, blk_id); @@ -1300,7 +1300,7 @@ // outer expression. visitor.scope_tree.record_rvalue_scope(expr.hir_id.local_id, blk_scope); - match expr.node { + match expr.kind { hir::ExprKind::AddrOf(_, ref subexpr) | hir::ExprKind::Unary(hir::UnDeref, ref subexpr) | hir::ExprKind::Field(ref subexpr, _) | @@ -1491,9 +1491,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ScopeTree { root_body, root_parent,
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index d833a34..a122d84 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs
@@ -459,7 +459,7 @@ } fn visit_item(&mut self, item: &'tcx hir::Item) { - match item.node { + match item.kind { hir::ItemKind::Fn(ref decl, _, ref generics, _) => { self.visit_early_late(None, decl, generics, |this| { intravisit::walk_item(this, item); @@ -504,12 +504,12 @@ | hir::ItemKind::Impl(_, _, _, ref generics, ..) => { // Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name". // This is not true for other kinds of items.x - let track_lifetime_uses = match item.node { + let track_lifetime_uses = match item.kind { hir::ItemKind::Impl(..) => true, _ => false, }; // These kinds of items have only early-bound lifetime parameters. - let mut index = if sub_items_have_self_param(&item.node) { + let mut index = if sub_items_have_self_param(&item.kind) { 1 // Self comes before lifetimes } else { 0 @@ -541,7 +541,7 @@ } fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) { - match item.node { + match item.kind { hir::ForeignItemKind::Fn(ref decl, _, ref generics) => { self.visit_early_late(None, decl, generics, |this| { intravisit::walk_foreign_item(this, item); @@ -558,8 +558,8 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty) { debug!("visit_ty: id={:?} ty={:?}", ty.hir_id, ty); - debug!("visit_ty: ty.node={:?}", ty.node); - match ty.node { + debug!("visit_ty: ty.kind={:?}", ty.kind); + match ty.kind { hir::TyKind::BareFn(ref c) => { let next_early_index = self.next_early_index(); let was_in_fn_syntax = self.is_in_fn_syntax; @@ -637,8 +637,7 @@ // `type MyAnonTy<'b> = impl MyTrait<'b>;` // ^ ^ this gets resolved in the scope of // the opaque_ty generics - let (generics, bounds) = match self.tcx.hir().expect_item(item_id.id).node - { + let (generics, bounds) = match self.tcx.hir().expect_item(item_id.id).kind { // Named opaque `impl Trait` types are reached via `TyKind::Path`. // This arm is for `impl Trait` in the types of statics, constants and locals. hir::ItemKind::OpaqueTy(hir::OpaqueTy { @@ -709,15 +708,22 @@ match param.kind { GenericParamKind::Lifetime { .. } => { let (name, reg) = Region::early(&self.tcx.hir(), &mut index, ¶m); + let def_id = if let Region::EarlyBound(_ ,def_id , _) = reg { + def_id + } else { + bug!(); + }; if let hir::ParamName::Plain(param_name) = name { if param_name.name == kw::UnderscoreLifetime { // Pick the elided lifetime "definition" if one exists // and use it to make an elision scope. + self.lifetime_uses.insert(def_id.clone(), LifetimeUseSet::Many); elision = Some(reg); } else { lifetimes.insert(name, reg); } } else { + self.lifetime_uses.insert(def_id.clone(), LifetimeUseSet::Many); lifetimes.insert(name, reg); } } @@ -765,20 +771,13 @@ }); } } - hir::TyKind::CVarArgs(ref lt) => { - // Resolve the generated lifetime for the C-variadic arguments. - // The lifetime is generated in AST -> HIR lowering. - if lt.name.is_elided() { - self.resolve_elided_lifetimes(vec![lt]) - } - } _ => intravisit::walk_ty(self, ty), } } fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { use self::hir::TraitItemKind::*; - match trait_item.node { + match trait_item.kind { Method(ref sig, _) => { let tcx = self.tcx; self.visit_early_late( @@ -830,7 +829,7 @@ fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { use self::hir::ImplItemKind::*; - match impl_item.node { + match impl_item.kind { Method(ref sig, _) => { let tcx = self.tcx; self.visit_early_late( @@ -1214,7 +1213,7 @@ } fn expression_label(ex: &hir::Expr) -> Option<ast::Ident> { - if let hir::ExprKind::Loop(_, Some(label), _) = ex.node { + if let hir::ExprKind::Loop(_, Some(label), _) = ex.kind { Some(label.ident) } else { None @@ -1263,7 +1262,7 @@ fn compute_object_lifetime_defaults(tcx: TyCtxt<'_>) -> HirIdMap<Vec<ObjectLifetimeDefault>> { let mut map = HirIdMap::default(); for item in tcx.hir().krate().items.values() { - match item.node { + match item.kind { hir::ItemKind::Struct(_, ref generics) | hir::ItemKind::Union(_, ref generics) | hir::ItemKind::Enum(_, ref generics) @@ -1352,7 +1351,7 @@ continue; } - let res = match data.bounded_ty.node { + let res = match data.bounded_ty.kind { hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => path.res, _ => continue, }; @@ -1487,7 +1486,7 @@ let mut elide_use = None; let mut find_arg_use_span = |inputs: &hir::HirVec<hir::Ty>| { for input in inputs { - match input.node { + match input.kind { hir::TyKind::Rptr(lt, _) => { if lt.name.ident() == name { // include the trailing whitespace between the lifetime and type names @@ -1525,12 +1524,12 @@ { match parent { Node::Item(item) => { - if let hir::ItemKind::Fn(decl, _, _, _) = &item.node { + if let hir::ItemKind::Fn(decl, _, _, _) = &item.kind { find_arg_use_span(&decl.inputs); } }, Node::ImplItem(impl_item) => { - if let hir::ImplItemKind::Method(sig, _) = &impl_item.node { + if let hir::ImplItemKind::Method(sig, _) = &impl_item.kind { find_arg_use_span(&sig.decl.inputs); } } @@ -1623,7 +1622,6 @@ _ => None, } { debug!("id = {:?} span = {:?} name = {:?}", id, span, name); - if name.name == kw::UnderscoreLifetime { continue; } @@ -1733,10 +1731,10 @@ let mut index = 0; if let Some(parent_id) = parent_id { let parent = self.tcx.hir().expect_item(parent_id); - if sub_items_have_self_param(&parent.node) { + if sub_items_have_self_param(&parent.kind) { index += 1; // Self comes before lifetimes } - match parent.node { + match parent.kind { hir::ItemKind::Trait(_, _, ref generics, ..) | hir::ItemKind::Impl(_, _, _, ref generics, ..) => { index += generics.params.len() as u32; @@ -1867,15 +1865,15 @@ let fn_id = self.tcx.hir().body_owner(body_id); match self.tcx.hir().get(fn_id) { Node::Item(&hir::Item { - node: hir::ItemKind::Fn(..), + kind: hir::ItemKind::Fn(..), .. }) | Node::TraitItem(&hir::TraitItem { - node: hir::TraitItemKind::Method(..), + kind: hir::TraitItemKind::Method(..), .. }) | Node::ImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Method(..), + kind: hir::ImplItemKind::Method(..), .. }) => { let scope = self.tcx.hir().local_def_id(fn_id); @@ -2165,18 +2163,18 @@ let body = match self.tcx.hir().get(parent) { // `fn` definitions and methods. Node::Item(&hir::Item { - node: hir::ItemKind::Fn(.., body), + kind: hir::ItemKind::Fn(.., body), .. }) => Some(body), Node::TraitItem(&hir::TraitItem { - node: hir::TraitItemKind::Method(_, ref m), + kind: hir::TraitItemKind::Method(_, ref m), .. }) => { if let hir::ItemKind::Trait(.., ref trait_items) = self.tcx .hir() .expect_item(self.tcx.hir().get_parent_item(parent)) - .node + .kind { assoc_item_kind = trait_items .iter() @@ -2190,13 +2188,13 @@ } Node::ImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Method(_, body), + kind: hir::ImplItemKind::Method(_, body), .. }) => { if let hir::ItemKind::Impl(.., ref self_ty, ref impl_items) = self.tcx .hir() .expect_item(self.tcx.hir().get_parent_item(parent)) - .node + .kind { impl_self = Some(self_ty); assoc_item_kind = impl_items @@ -2270,8 +2268,8 @@ } fn visit_ty(&mut self, ty: &'a hir::Ty) { - if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.node { - if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node + if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.kind { + if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.kind { if self.is_self_ty(path.res) { if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { @@ -2286,7 +2284,7 @@ let mut visitor = SelfVisitor { map: self.map, - impl_self: impl_self.map(|ty| &ty.node), + impl_self: impl_self.map(|ty| &ty.kind), lifetime: Set1::Empty, }; visitor.visit_ty(&inputs[0]); @@ -2364,10 +2362,10 @@ } fn visit_ty(&mut self, ty: &hir::Ty) { - if let hir::TyKind::BareFn(_) = ty.node { + if let hir::TyKind::BareFn(_) = ty.kind { self.outer_index.shift_in(1); } - match ty.node { + match ty.kind { hir::TyKind::TraitObject(ref bounds, ref lifetime) => { for bound in bounds { self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); @@ -2379,12 +2377,11 @@ self.visit_lifetime(lifetime); } } - hir::TyKind::CVarArgs(_) => {} _ => { intravisit::walk_ty(self, ty); } } - if let hir::TyKind::BareFn(_) = ty.node { + if let hir::TyKind::BareFn(_) = ty.kind { self.outer_index.shift_out(1); } } @@ -2991,7 +2988,7 @@ } fn visit_ty(&mut self, ty: &'v hir::Ty) { - match ty.node { + match ty.kind { hir::TyKind::Path(hir::QPath::Resolved(Some(_), _)) | hir::TyKind::Path(hir::QPath::TypeRelative(..)) => { // ignore lifetimes appearing in associated type
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index c06a0fe..93d0627 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs
@@ -25,7 +25,7 @@ use std::mem::replace; use std::cmp::Ordering; -#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Copy, Debug, Eq, Hash)] +#[derive(PartialEq, Clone, Copy, Debug)] pub enum StabilityLevel { Unstable, Stable, @@ -199,8 +199,12 @@ let name = attr.name_or_empty(); if [sym::unstable, sym::stable, sym::rustc_deprecated].contains(&name) { attr::mark_used(attr); - self.tcx.sess.span_err(attr.span, "stability attributes may not be used \ - outside of the standard library"); + struct_span_err!( + self.tcx.sess, + attr.span, + E0734, + "stability attributes may not be used outside of the standard library", + ).emit(); } } @@ -246,7 +250,7 @@ fn visit_item(&mut self, i: &'tcx Item) { let orig_in_trait_impl = self.in_trait_impl; let mut kind = AnnotationKind::Required; - match i.node { + match i.kind { // Inherent impls and foreign modules serve only as containers for other items, // they don't have their own stability. They still can be annotated as unstable // and propagate this unstability to children, but this annotation is completely @@ -344,14 +348,14 @@ } fn visit_item(&mut self, i: &'tcx Item) { - match i.node { + match i.kind { // Inherent impls and foreign modules serve only as containers for other items, // they don't have their own stability. They still can be annotated as unstable // and propagate this unstability to children, but this annotation is completely // optional. They inherit stability from their parents when unannotated. hir::ItemKind::Impl(.., None, _, _) | hir::ItemKind::ForeignMod(..) => {} - _ => self.check_missing_stability(i.hir_id, i.span, i.node.descriptive_variant()) + _ => self.check_missing_stability(i.hir_id, i.span, i.kind.descriptive_variant()) } intravisit::walk_item(self, i) @@ -382,7 +386,7 @@ } fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem) { - self.check_missing_stability(i.hir_id, i.span, i.node.descriptive_variant()); + self.check_missing_stability(i.hir_id, i.span, i.kind.descriptive_variant()); intravisit::walk_foreign_item(self, i); } @@ -481,7 +485,13 @@ } pub fn report_unstable( - sess: &Session, feature: Symbol, reason: Option<Symbol>, issue: u32, is_soft: bool, span: Span + sess: &Session, + feature: Symbol, + reason: Option<Symbol>, + issue: u32, + is_soft: bool, + span: Span, + soft_handler: impl FnOnce(&'static lint::Lint, Span, &str), ) { let msg = match reason { Some(r) => format!("use of unstable library feature '{}': {}", feature, r), @@ -507,7 +517,7 @@ let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id); if fresh { if is_soft { - sess.buffer_lint(lint::builtin::SOFT_UNSTABLE, CRATE_NODE_ID, span, &msg); + soft_handler(lint::builtin::SOFT_UNSTABLE, span, &msg) } else { emit_feature_err( &sess.parse_sess, feature, span, GateIssue::Library(Some(issue)), &msg @@ -576,7 +586,7 @@ } pub fn early_report_deprecation( - sess: &Session, + lint_buffer: &'a mut lint::LintBuffer, message: &str, suggestion: Option<Symbol>, lint: &'static Lint, @@ -587,7 +597,7 @@ } let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span); - sess.buffer_lint_with_diagnostic(lint, CRATE_NODE_ID, span, message, diag); + lint_buffer.buffer_lint_with_diagnostic(lint, CRATE_NODE_ID, span, message, diag); } fn late_report_deprecation( @@ -775,10 +785,12 @@ /// Additionally, this function will also check if the item is deprecated. If so, and `id` is /// not `None`, a deprecated lint attached to `id` will be emitted. pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) { + let soft_handler = + |lint, span, msg: &_| self.lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg); match self.eval_stability(def_id, id, span) { EvalResult::Allow => {} EvalResult::Deny { feature, reason, issue, is_soft } => - report_unstable(self.sess, feature, reason, issue, is_soft, span), + report_unstable(self.sess, feature, reason, issue, is_soft, span, soft_handler), EvalResult::Unmarked => { // The API could be uncallable for other reasons, for example when a private module // was referenced. @@ -797,7 +809,7 @@ } fn visit_item(&mut self, item: &'tcx hir::Item) { - match item.node { + match item.kind { hir::ItemKind::ExternCrate(_) => { // compiler-generated `extern crate` items have a dummy span. if item.span.is_dummy() { return } @@ -893,11 +905,10 @@ // Warn if the user has enabled an already-stable lang feature. unnecessary_stable_feature_lint(tcx, span, feature, since); } - if lang_features.contains(&feature) { + if !lang_features.insert(feature) { // Warn if the user enables a lang feature multiple times. duplicate_feature_err(tcx.sess, span, feature); } - lang_features.insert(feature); } let declared_lib_features = &tcx.features().declared_lib_features;
diff --git a/src/librustc/mir/cache.rs b/src/librustc/mir/cache.rs index 1f60487..9b41366 100644 --- a/src/librustc/mir/cache.rs +++ b/src/librustc/mir/cache.rs
@@ -1,6 +1,6 @@ -use rustc_data_structures::indexed_vec::IndexVec; +use rustc_index::vec::IndexVec; use rustc_data_structures::sync::{RwLock, MappedReadGuard, ReadGuard}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; use crate::ich::StableHashingContext; use crate::mir::{Body, BasicBlock}; @@ -24,9 +24,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for Cache { - fn hash_stable<W: StableHasherResult>(&self, - _: &mut StableHashingContext<'a>, - _: &mut StableHasher<W>) { + fn hash_stable(&self, _: &mut StableHashingContext<'a>, _: &mut StableHasher) { // Do nothing. } }
diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 15e6cb6..aa8ac49 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs
@@ -245,6 +245,8 @@ /// as a slice. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods + /// on `InterpCx` instead. #[inline] pub fn get_bytes( &self, @@ -275,6 +277,8 @@ /// so be sure to actually put data there! /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods + /// on `InterpCx` instead. pub fn get_bytes_mut( &mut self, cx: &impl HasDataLayout, @@ -297,6 +301,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { /// Reads bytes until a `0` is encountered. Will error if the end of the allocation is reached /// before a `0` is found. + /// + /// Most likely, you want to call `Memory::read_c_str` instead of this method. pub fn read_c_str( &self, cx: &impl HasDataLayout, @@ -342,33 +348,25 @@ /// Writes `src` to the memory starting at `ptr.offset`. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to call `Memory::write_bytes` instead of this method. pub fn write_bytes( &mut self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, - src: &[u8], + src: impl IntoIterator<Item=u8>, ) -> InterpResult<'tcx> { - let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64))?; - bytes.clone_from_slice(src); - Ok(()) - } - - /// 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, - ptr: Pointer<Tag>, - val: u8, - count: Size - ) -> InterpResult<'tcx> - { - let bytes = self.get_bytes_mut(cx, ptr, count)?; - for b in bytes { - *b = val; + let mut src = src.into_iter(); + let (lower, upper) = src.size_hint(); + let len = upper.expect("can only write bounded iterators"); + assert_eq!(lower, len, "can only write iterators with a precise length"); + let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(len as u64))?; + // `zip` would stop when the first iterator ends; we want to definitely + // cover all of `bytes`. + for dest in bytes { + *dest = src.next().expect("iterator was shorter than it said it would be"); } + src.next().expect_none("iterator was longer than it said it would be"); Ok(()) } @@ -380,6 +378,7 @@ /// pointers being valid for ZSTs. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to call `InterpCx::read_scalar` instead of this method. pub fn read_scalar( &self, cx: &impl HasDataLayout, @@ -418,6 +417,7 @@ /// Reads a pointer-sized scalar. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to call `InterpCx::read_scalar` instead of this method. pub fn read_ptr_sized( &self, cx: &impl HasDataLayout, @@ -435,6 +435,7 @@ /// pointers being valid for ZSTs. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to call `InterpCx::write_scalar` instead of this method. pub fn write_scalar( &mut self, cx: &impl HasDataLayout, @@ -477,6 +478,7 @@ /// Writes a pointer-sized scalar. /// /// It is the caller's responsibility to check bounds and alignment beforehand. + /// Most likely, you want to call `InterpCx::write_scalar` instead of this method. pub fn write_ptr_sized( &mut self, cx: &impl HasDataLayout,
diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index ac99ccd..d918b9e 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs
@@ -363,6 +363,8 @@ UbExperimental(String), /// Unreachable code was executed. Unreachable, + /// An enum discriminant was set to a value which was outside the range of valid values. + InvalidDiscriminant(ScalarMaybeUndef), } impl fmt::Debug for UndefinedBehaviorInfo { @@ -373,6 +375,8 @@ write!(f, "{}", msg), Unreachable => write!(f, "entered unreachable code"), + InvalidDiscriminant(val) => + write!(f, "encountered invalid enum discriminant {}", val), } } } @@ -400,7 +404,6 @@ InvalidMemoryAccess, InvalidFunctionPointer, InvalidBool, - InvalidDiscriminant(ScalarMaybeUndef), PointerOutOfBounds { ptr: Pointer, msg: CheckInAllocMsg, @@ -485,8 +488,6 @@ write!(f, "incorrect alloc info: expected size {} and align {}, \ got size {} and align {}", size.bytes(), align.bytes(), size2.bytes(), align2.bytes()), - InvalidDiscriminant(val) => - write!(f, "encountered invalid enum discriminant {}", val), InvalidMemoryAccess => write!(f, "tried to access memory through an invalid pointer"), DanglingPointerDeref =>
diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 23433c2..6c31d54 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs
@@ -101,7 +101,7 @@ InvalidProgramInfo, ResourceExhaustionInfo, UndefinedBehaviorInfo, }; -pub use self::value::{Scalar, ScalarMaybeUndef, RawConst, ConstValue}; +pub use self::value::{Scalar, ScalarMaybeUndef, RawConst, ConstValue, get_slice_bytes}; pub use self::allocation::{Allocation, AllocationExtra, Relocations, UndefMask}; @@ -109,7 +109,7 @@ use crate::mir; use crate::hir::def_id::DefId; -use crate::ty::{self, TyCtxt, Instance, subst::UnpackedKind}; +use crate::ty::{self, TyCtxt, Instance, subst::GenericArgKind}; use crate::ty::codec::TyDecoder; use crate::ty::layout::{self, Size}; use std::io; @@ -426,7 +426,7 @@ // this for generic functions. Lifetime parameters are ignored. let is_generic = instance.substs.into_iter().any(|kind| { match kind.unpack() { - UnpackedKind::Lifetime(_) => false, + GenericArgKind::Lifetime(_) => false, _ => true, } }); @@ -470,6 +470,14 @@ } } + /// Panics if the `AllocId` does not refer to a function + pub fn unwrap_fn(&self, id: AllocId) -> Instance<'tcx> { + match self.get(id) { + Some(GlobalAlloc::Function(instance)) => instance, + _ => bug!("expected allocation ID {} to point to a function", id), + } + } + /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to /// call this function twice, even with the same `Allocation` will ICE the compiler. pub fn set_alloc_id_memory(&mut self, id: AllocId, mem: &'tcx Allocation) {
diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index b8bc741..ac16b8b 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs
@@ -5,11 +5,12 @@ use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size}, subst::SubstsRef}; use crate::ty::PlaceholderConst; use crate::hir::def_id::DefId; +use crate::ty::{BoundVar, DebruijnIndex}; use super::{InterpResult, Pointer, PointerArithmetic, Allocation, AllocId, sign_extend, truncate}; /// Represents the result of a raw const operation, pre-validation. -#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash, HashStable)] +#[derive(Clone, HashStable)] pub struct RawConst<'tcx> { // the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory` // (so you can use `AllocMap::unwrap_memory`). @@ -28,6 +29,9 @@ /// Infer the value of the const. Infer(InferConst<'tcx>), + /// Bound const variable, used only when preparing a trait query. + Bound(DebruijnIndex, BoundVar), + /// A placeholder const - universally quantified higher-ranked const. Placeholder(PlaceholderConst), @@ -66,8 +70,9 @@ match *self { ConstValue::Param(_) | ConstValue::Infer(_) | + ConstValue::Bound(..) | ConstValue::Placeholder(_) | - ConstValue::ByRef{ .. } | + ConstValue::ByRef { .. } | ConstValue::Unevaluated(..) | ConstValue::Slice { .. } => None, ConstValue::Scalar(val) => Some(val), @@ -343,14 +348,19 @@ } } + #[inline(always)] + pub fn check_raw(data: u128, size: u8, target_size: Size) { + assert_eq!(target_size.bytes(), size as u64); + assert_ne!(size, 0, "you should never look at the bits of a ZST"); + Scalar::check_data(data, size); + } + /// Do not call this method! Use either `assert_bits` or `force_bits`. #[inline] pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { match self { Scalar::Raw { data, size } => { - assert_eq!(target_size.bytes(), size as u64); - assert_ne!(size, 0, "you should never look at the bits of a ZST"); - Scalar::check_data(data, size); + Self::check_raw(data, size, target_size); Ok(data) } Scalar::Ptr(_) => throw_unsup!(ReadPointerAsBytes), @@ -482,7 +492,7 @@ } } -#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, Eq, PartialEq, RustcEncodable, RustcDecodable)] pub enum ScalarMaybeUndef<Tag = (), Id = AllocId> { Scalar(Scalar<Tag, Id>), Undef, @@ -611,3 +621,18 @@ Scalar(v), Undef }); + +/// Gets the bytes of a constant slice value. +pub fn get_slice_bytes<'tcx>(cx: &impl HasDataLayout, val: ConstValue<'tcx>) -> &'tcx [u8] { + if let ConstValue::Slice { data, start, end } = val { + let len = end - start; + data.get_bytes( + cx, + // invent a pointer, only the offset is relevant anyway + Pointer::new(AllocId(0), Size::from_bytes(start as u64)), + Size::from_bytes(len as u64), + ).unwrap_or_else(|err| bug!("const slice is invalid: {:?}", err)) + } else { + bug!("expected const slice, but found another const value"); + } +}
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 92efcf4..f7e0d01 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs
@@ -15,16 +15,15 @@ use crate::ty::print::{FmtPrinter, Printer}; use crate::ty::subst::{Subst, SubstsRef}; use crate::ty::{ - self, AdtDef, CanonicalUserTypeAnnotations, ClosureSubsts, GeneratorSubsts, Region, Ty, TyCtxt, - UserTypeAnnotationIndex, + self, AdtDef, CanonicalUserTypeAnnotations, List, Region, Ty, TyCtxt, UserTypeAnnotationIndex, }; use polonius_engine::Atom; -use rustc_data_structures::bit_set::BitMatrix; +use rustc_index::bit_set::BitMatrix; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::dominators::{dominators, Dominators}; use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors}; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_index::vec::{Idx, IndexVec}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::MappedReadGuard; use rustc_macros::HashStable; @@ -37,7 +36,7 @@ use std::vec::IntoIter; use std::{iter, mem, option, u32}; use syntax::ast::Name; -use syntax::symbol::{InternedString, Symbol}; +use syntax::symbol::Symbol; use syntax_pos::{Span, DUMMY_SP}; pub use crate::mir::interpret::AssertMessage; @@ -262,6 +261,12 @@ dominators(self) } + /// Returns `true` if a cycle exists in the control-flow graph that is reachable from the + /// `START_BLOCK`. + pub fn is_cfg_cyclic(&self) -> bool { + graph::is_cyclic(self) + } + #[inline] pub fn local_kind(&self, local: Local) -> LocalKind { let index = local.as_usize(); @@ -462,7 +467,9 @@ /// Grouped information about the source code origin of a MIR entity. /// Intended to be inspected by diagnostics and debuginfo. /// Most passes can work with it as a whole, within a single function. -#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, HashStable)] +// The unoffical Cranelift backend, at least as of #65828, needs `SourceInfo` to implement `Eq` and +// `Hash`. Please ping @bjorn3 if removing them. +#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash, HashStable)] pub struct SourceInfo { /// The source span for the AST pertaining to this MIR entity. pub span: Span, @@ -575,7 +582,7 @@ /////////////////////////////////////////////////////////////////////////// // Variables and temps -newtype_index! { +rustc_index::newtype_index! { pub struct Local { derive [HashStable] DEBUG_FORMAT = "_{}", @@ -602,7 +609,7 @@ ReturnPointer, } -#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct VarBindingForm<'tcx> { /// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`? pub binding_mode: ty::BindingMode, @@ -624,7 +631,7 @@ pub pat_span: Span, } -#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub enum BindingForm<'tcx> { /// This is a binding for a non-`self` binding, or a `self` that has an explicit type. Var(VarBindingForm<'tcx>), @@ -635,7 +642,7 @@ } /// Represents what type of implicit self a function has, if any. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable)] pub enum ImplicitSelfKind { /// Represents a `fn x(self);`. Imm, @@ -676,14 +683,10 @@ mod binding_form_impl { use crate::ich::StableHashingContext; - use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; + use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for super::BindingForm<'tcx> { - fn hash_stable<W: StableHasherResult>( - &self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>, - ) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use super::BindingForm::*; ::std::mem::discriminant(self).hash_stable(hcx, hasher); @@ -992,7 +995,7 @@ /////////////////////////////////////////////////////////////////////////// // BasicBlock -newtype_index! { +rustc_index::newtype_index! { pub struct BasicBlock { derive [HashStable] DEBUG_FORMAT = "bb{}", @@ -1498,7 +1501,7 @@ Goto { .. } => vec!["".into()], SwitchInt { ref values, switch_ty, .. } => ty::tls::with(|tcx| { let param_env = ty::ParamEnv::empty(); - let switch_ty = tcx.lift_to_global(&switch_ty).unwrap(); + let switch_ty = tcx.lift(&switch_ty).unwrap(); let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; values .iter() @@ -1710,15 +1713,17 @@ /// A path to a value; something that can be evaluated without /// changing or disturbing program state. #[derive( - Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, HashStable, + Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, HashStable, )] pub struct Place<'tcx> { pub base: PlaceBase<'tcx>, /// projection out of a place (access a field, deref a pointer, etc) - pub projection: Box<[PlaceElem<'tcx>]>, + pub projection: &'tcx List<PlaceElem<'tcx>>, } +impl<'tcx> rustc_serialize::UseSpecializedDecodable for Place<'tcx> {} + #[derive( Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, HashStable, )] @@ -1822,6 +1827,8 @@ /// and the index is a local. pub type PlaceElem<'tcx> = ProjectionElem<Local, Ty<'tcx>>; +impl<'tcx> Copy for PlaceElem<'tcx> { } + // At least on 64 bit systems, `PlaceElem` should not be larger than two pointers. #[cfg(target_arch = "x86_64")] static_assert_size!(PlaceElem<'_>, 16); @@ -1830,7 +1837,7 @@ /// need neither the `V` parameter for `Index` nor the `T` for `Field`. pub type ProjectionKind = ProjectionElem<(), ()>; -newtype_index! { +rustc_index::newtype_index! { pub struct Field { derive [HashStable] DEBUG_FORMAT = "field[{}]" @@ -1844,50 +1851,11 @@ } impl<'tcx> Place<'tcx> { - // FIXME change this back to a const when projection is a shared slice. - // - // pub const RETURN_PLACE: Place<'tcx> = Place { - // base: PlaceBase::Local(RETURN_PLACE), - // projection: &[], - // }; + // FIXME change this to a const fn by also making List::empty a const fn. pub fn return_place() -> Place<'tcx> { Place { base: PlaceBase::Local(RETURN_PLACE), - projection: Box::new([]), - } - } - - pub fn field(self, f: Field, ty: Ty<'tcx>) -> Place<'tcx> { - self.elem(ProjectionElem::Field(f, ty)) - } - - pub fn deref(self) -> Place<'tcx> { - self.elem(ProjectionElem::Deref) - } - - pub fn downcast(self, adt_def: &'tcx AdtDef, variant_index: VariantIdx) -> Place<'tcx> { - self.elem(ProjectionElem::Downcast( - Some(adt_def.variants[variant_index].ident.name), - variant_index, - )) - } - - pub fn downcast_unnamed(self, variant_index: VariantIdx) -> Place<'tcx> { - self.elem(ProjectionElem::Downcast(None, variant_index)) - } - - pub fn index(self, index: Local) -> Place<'tcx> { - self.elem(ProjectionElem::Index(index)) - } - - pub fn elem(self, elem: PlaceElem<'tcx>) -> Place<'tcx> { - // FIXME(spastorino): revisit this again once projection is not a Box<[T]> anymore - let mut projection = self.projection.into_vec(); - projection.push(elem); - - Place { - base: self.base, - projection: projection.into_boxed_slice(), + projection: List::empty(), } } @@ -1904,15 +1872,15 @@ // // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? pub fn local_or_deref_local(&self) -> Option<Local> { - match self { - Place { - base: PlaceBase::Local(local), - projection: box [], + match self.as_ref() { + PlaceRef { + base: &PlaceBase::Local(local), + projection: &[], } | - Place { - base: PlaceBase::Local(local), - projection: box [ProjectionElem::Deref], - } => Some(*local), + PlaceRef { + base: &PlaceBase::Local(local), + projection: &[ProjectionElem::Deref], + } => Some(local), _ => None, } } @@ -1920,10 +1888,7 @@ /// If this place represents a local variable like `_X` with no /// projections, return `Some(_X)`. pub fn as_local(&self) -> Option<Local> { - match self { - Place { projection: box [], base: PlaceBase::Local(l) } => Some(*l), - _ => None, - } + self.as_ref().as_local() } pub fn as_ref(&self) -> PlaceRef<'_, 'tcx> { @@ -1938,7 +1903,7 @@ fn from(local: Local) -> Self { Place { base: local.into(), - projection: Box::new([]), + projection: List::empty(), } } } @@ -1967,6 +1932,15 @@ _ => None, } } + + /// If this place represents a local variable like `_X` with no + /// projections, return `Some(_X)`. + pub fn as_local(&self) -> Option<Local> { + match self { + PlaceRef { base: PlaceBase::Local(l), projection: [] } => Some(*l), + _ => None, + } + } } impl Debug for Place<'_> { @@ -2045,7 +2019,7 @@ /////////////////////////////////////////////////////////////////////////// // Scopes -newtype_index! { +rustc_index::newtype_index! { pub struct SourceScope { derive [HashStable] DEBUG_FORMAT = "scope[{}]", @@ -2186,8 +2160,8 @@ /// active field index would identity the field `c` Adt(&'tcx AdtDef, VariantIdx, SubstsRef<'tcx>, Option<UserTypeAnnotationIndex>, Option<usize>), - Closure(DefId, ClosureSubsts<'tcx>), - Generator(DefId, GeneratorSubsts<'tcx>, hir::GeneratorMovability), + Closure(DefId, SubstsRef<'tcx>), + Generator(DefId, SubstsRef<'tcx>, hir::GeneratorMovability), } #[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] @@ -2390,7 +2364,7 @@ /// this does not necessarily mean that they are "==" in Rust -- in /// particular one must be wary of `NaN`! -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] pub struct Constant<'tcx> { pub span: Span, @@ -2436,7 +2410,7 @@ /// The first will lead to the constraint `w: &'1 str` (for some /// inferred region `'1`). The second will lead to the constraint `w: /// &'static str`. -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct UserTypeProjections { pub(crate) contents: Vec<(UserTypeProjection, Span)>, } @@ -2513,7 +2487,7 @@ /// * `let (x, _): T = ...` -- here, the `projs` vector would contain /// `field[0]` (aka `.0`), indicating that the type of `s` is /// determined by finding the type of the `.0` field from `T`. -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct UserTypeProjection { pub base: UserTypeAnnotationIndex, pub projs: Vec<ProjectionKind>, @@ -2584,7 +2558,7 @@ } } -newtype_index! { +rustc_index::newtype_index! { pub struct Promoted { derive [HashStable] DEBUG_FORMAT = "promoted[{}]" @@ -2600,7 +2574,14 @@ impl<'tcx> Display for Constant<'tcx> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { write!(fmt, "const ")?; - write!(fmt, "{}", self.literal) + // FIXME make the default pretty printing of raw pointers more detailed. Here we output the + // debug representation of raw pointers, so that the raw pointers in the mir dump output are + // detailed and just not '{pointer}'. + if let ty::RawPtr(_) = self.literal.ty.kind { + write!(fmt, "{:?} : {}", self.literal.val, self.literal.ty) + } else { + write!(fmt, "{}", self.literal) + } } } @@ -2715,7 +2696,7 @@ } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] pub enum UnsafetyViolationKind { General, /// Permitted both in `const fn`s and regular `fn`s. @@ -2724,15 +2705,15 @@ BorrowPacked(hir::HirId), } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] pub struct UnsafetyViolation { pub source_info: SourceInfo, - pub description: InternedString, - pub details: InternedString, + pub description: Symbol, + pub details: Symbol, pub kind: UnsafetyViolationKind, } -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] pub struct UnsafetyCheckResult { /// Violations that are propagated *upwards* from this function. pub violations: Lrc<[UnsafetyViolation]>, @@ -2741,7 +2722,7 @@ pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>, } -newtype_index! { +rustc_index::newtype_index! { pub struct GeneratorSavedLocal { derive [HashStable] DEBUG_FORMAT = "_{}", @@ -3173,6 +3154,17 @@ } } +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<PlaceElem<'tcx>> { + fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { + let v = self.iter().map(|t| t.fold_with(folder)).collect::<Vec<_>>(); + folder.tcx().intern_place_elems(&v) + } + + fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + impl<'tcx> TypeFoldable<'tcx> for Static<'tcx> { fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { Static {
diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs index a061e6f..58f9966 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc/mir/mono.rs
@@ -1,6 +1,6 @@ use crate::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; use crate::hir::HirId; -use syntax::symbol::InternedString; +use syntax::symbol::Symbol; use syntax::attr::InlineAttr; use syntax::source_map::Span; use crate::ty::{Instance, InstanceDef, TyCtxt, SymbolName, subst::InternalSubsts}; @@ -8,15 +8,14 @@ use crate::ty::print::obsolete::DefPathBasedNames; use crate::dep_graph::{WorkProductId, DepNode, WorkProduct, DepConstructor}; use rustc_data_structures::base_n; -use rustc_data_structures::stable_hasher::{HashStable, StableHasherResult, - StableHasher}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use crate::ich::{Fingerprint, StableHashingContext, NodeIdHashingMode}; use crate::session::config::OptLevel; use std::fmt; use std::hash::Hash; /// Describes how a monomorphization will be instantiated in object files. -#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] +#[derive(PartialEq)] pub enum InstantiationMode { /// There will be exactly one instance of the given MonoItem. It will have /// external linkage so that it can be linked to from other codegen units. @@ -81,7 +80,7 @@ MonoItem::GlobalAsm(hir_id) => { let def_id = tcx.hir().local_def_id(hir_id); SymbolName { - name: InternedString::intern(&format!("global_asm_{:?}", def_id)) + name: Symbol::intern(&format!("global_asm_{:?}", def_id)) } } } @@ -223,9 +222,7 @@ } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for MonoItem<'tcx> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { ::std::mem::discriminant(self).hash_stable(hcx, hasher); match *self { @@ -249,12 +246,12 @@ /// name be unique amongst **all** crates. Therefore, it should /// contain something unique to this crate (e.g., a module path) /// as well as the crate name and disambiguator. - name: InternedString, + name: Symbol, items: FxHashMap<MonoItem<'tcx>, (Linkage, Visibility)>, size_estimate: Option<usize>, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] pub enum Linkage { External, AvailableExternally, @@ -283,7 +280,7 @@ Common }); -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum Visibility { Default, Hidden, @@ -297,7 +294,7 @@ }); impl<'tcx> CodegenUnit<'tcx> { - pub fn new(name: InternedString) -> CodegenUnit<'tcx> { + pub fn new(name: Symbol) -> CodegenUnit<'tcx> { CodegenUnit { name: name, items: Default::default(), @@ -305,11 +302,11 @@ } } - pub fn name(&self) -> &InternedString { - &self.name + pub fn name(&self) -> Symbol { + self.name } - pub fn set_name(&mut self, name: InternedString) { + pub fn set_name(&mut self, name: Symbol) { self.name = name; } @@ -389,6 +386,7 @@ tcx.hir().as_local_hir_id(def_id) } InstanceDef::VtableShim(..) | + InstanceDef::ReifyShim(..) | InstanceDef::Intrinsic(..) | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) | @@ -419,9 +417,7 @@ } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for CodegenUnit<'tcx> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let CodegenUnit { ref items, name, @@ -478,7 +474,7 @@ cnum: CrateNum, components: I, special_suffix: Option<S>) - -> InternedString + -> Symbol where I: IntoIterator<Item=C>, C: fmt::Display, S: fmt::Display, @@ -491,7 +487,7 @@ cgu_name } else { let cgu_name = &cgu_name.as_str()[..]; - InternedString::intern(&CodegenUnit::mangle_name(cgu_name)) + Symbol::intern(&CodegenUnit::mangle_name(cgu_name)) } } @@ -501,7 +497,7 @@ cnum: CrateNum, components: I, special_suffix: Option<S>) - -> InternedString + -> Symbol where I: IntoIterator<Item=C>, C: fmt::Display, S: fmt::Display, @@ -547,6 +543,6 @@ write!(cgu_name, ".{}", special_suffix).unwrap(); } - InternedString::intern(&cgu_name[..]) + Symbol::intern(&cgu_name[..]) } }
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index d776809..e87aabf 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs
@@ -34,7 +34,7 @@ /// /// Note that the resulting type has not been normalized. pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: &Field) -> Ty<'tcx> { - let answer = match self.ty.sty { + let answer = match self.ty.kind { ty::Adt(adt_def, substs) => { let variant_def = match self.variant_index { None => adt_def.non_enum_variant(), @@ -89,7 +89,7 @@ ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => PlaceTy::from_ty(self.ty.builtin_index().unwrap()), ProjectionElem::Subslice { from, to } => { - PlaceTy::from_ty(match self.ty.sty { + PlaceTy::from_ty(match self.ty.kind { ty::Array(inner, size) => { let size = size.eval_usize(tcx, param_env); let len = size - (from as u64) - (to as u64); @@ -195,9 +195,9 @@ } Rvalue::Discriminant(ref place) => { let ty = place.ty(local_decls, tcx).ty; - match ty.sty { + match ty.kind { ty::Adt(adt_def, _) => adt_def.repr.discr_type().to_ty(tcx), - ty::Generator(_, substs, _) => substs.discr_ty(tcx), + ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), _ => { // This can only be `0`, for now, so `u8` will suffice. tcx.types.u8
diff --git a/src/librustc/mir/traversal.rs b/src/librustc/mir/traversal.rs index 1416a5f..f129dd3 100644 --- a/src/librustc/mir/traversal.rs +++ b/src/librustc/mir/traversal.rs
@@ -1,4 +1,4 @@ -use rustc_data_structures::bit_set::BitSet; +use rustc_index::bit_set::BitSet; use super::*;
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 1e3b9eb..6a41b84 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs
@@ -1,5 +1,5 @@ use crate::ty::subst::SubstsRef; -use crate::ty::{CanonicalUserTypeAnnotation, ClosureSubsts, GeneratorSubsts, Ty}; +use crate::ty::{CanonicalUserTypeAnnotation, Ty}; use crate::mir::*; use syntax_pos::Span; @@ -158,13 +158,7 @@ self.super_place_base(base, context, location); } - fn visit_projection(&mut self, - base: & $($mutability)? PlaceBase<'tcx>, - projection: & $($mutability)? [PlaceElem<'tcx>], - context: PlaceContext, - location: Location) { - self.super_projection(base, projection, context, location); - } + visit_place_fns!($($mutability)?); fn visit_constant(&mut self, constant: & $($mutability)? Constant<'tcx>, @@ -221,18 +215,6 @@ self.super_substs(substs); } - fn visit_closure_substs(&mut self, - substs: & $($mutability)? ClosureSubsts<'tcx>, - _: Location) { - self.super_closure_substs(substs); - } - - fn visit_generator_substs(&mut self, - substs: & $($mutability)? GeneratorSubsts<'tcx>, - _: Location) { - self.super_generator_substs(substs); - } - fn visit_local_decl(&mut self, local: Local, local_decl: & $($mutability)? LocalDecl<'tcx>) { @@ -618,14 +600,14 @@ _, closure_substs ) => { - self.visit_closure_substs(closure_substs, location); + self.visit_substs(closure_substs, location); } AggregateKind::Generator( _, generator_substs, _movability, ) => { - self.visit_generator_substs(generator_substs, location); + self.visit_substs(generator_substs, location); } } @@ -684,28 +666,6 @@ ); } - fn super_place(&mut self, - place: & $($mutability)? Place<'tcx>, - context: PlaceContext, - location: Location) { - let mut context = context; - - if !place.projection.is_empty() { - context = if context.is_mutating_use() { - PlaceContext::MutatingUse(MutatingUseContext::Projection) - } else { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) - }; - } - - self.visit_place_base(& $($mutability)? place.base, context, location); - - self.visit_projection(& $($mutability)? place.base, - & $($mutability)? place.projection, - context, - location); - } - fn super_place_base(&mut self, place_base: & $($mutability)? PlaceBase<'tcx>, context: PlaceContext, @@ -720,36 +680,6 @@ } } - fn super_projection(&mut self, - base: & $($mutability)? PlaceBase<'tcx>, - projection: & $($mutability)? [PlaceElem<'tcx>], - context: PlaceContext, - location: Location) { - if let [proj_base @ .., elem] = projection { - self.visit_projection(base, proj_base, context, location); - - match elem { - ProjectionElem::Field(_field, ty) => { - self.visit_ty(ty, TyContext::Location(location)); - } - ProjectionElem::Index(local) => { - self.visit_local( - local, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), - location - ); - } - ProjectionElem::Deref | - ProjectionElem::Subslice { from: _, to: _ } | - ProjectionElem::ConstantIndex { offset: _, - min_length: _, - from_end: _ } | - ProjectionElem::Downcast(_, _) => { - } - } - } - } - fn super_local_decl(&mut self, local: Local, local_decl: & $($mutability)? LocalDecl<'tcx>) { @@ -834,14 +764,6 @@ fn super_substs(&mut self, _substs: & $($mutability)? SubstsRef<'tcx>) { } - fn super_generator_substs(&mut self, - _substs: & $($mutability)? GeneratorSubsts<'tcx>) { - } - - fn super_closure_substs(&mut self, - _substs: & $($mutability)? ClosureSubsts<'tcx>) { - } - // Convenience methods fn visit_location(&mut self, body: & $($mutability)? Body<'tcx>, location: Location) { @@ -860,6 +782,145 @@ } } +macro_rules! visit_place_fns { + (mut) => ( + fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; + + fn super_place( + &mut self, + place: &mut Place<'tcx>, + context: PlaceContext, + location: Location, + ) { + self.visit_place_base(&mut place.base, context, location); + + if let Some(new_projection) = self.process_projection(&place.projection) { + place.projection = self.tcx().intern_place_elems(&new_projection); + } + } + + fn process_projection( + &mut self, + projection: &'a [PlaceElem<'tcx>], + ) -> Option<Vec<PlaceElem<'tcx>>> { + let mut projection = Cow::Borrowed(projection); + + for i in 0..projection.len() { + if let Some(elem) = projection.get(i) { + if let Some(elem) = self.process_projection_elem(elem) { + // This converts the borrowed projection into `Cow::Owned(_)` and returns a + // clone of the projection so we can mutate and reintern later. + let vec = projection.to_mut(); + vec[i] = elem; + } + } + } + + match projection { + Cow::Borrowed(_) => None, + Cow::Owned(vec) => Some(vec), + } + } + + fn process_projection_elem( + &mut self, + _elem: &PlaceElem<'tcx>, + ) -> Option<PlaceElem<'tcx>> { + None + } + ); + + () => ( + fn visit_projection( + &mut self, + base: &PlaceBase<'tcx>, + projection: &[PlaceElem<'tcx>], + context: PlaceContext, + location: Location, + ) { + self.super_projection(base, projection, context, location); + } + + fn visit_projection_elem( + &mut self, + base: &PlaceBase<'tcx>, + proj_base: &[PlaceElem<'tcx>], + elem: &PlaceElem<'tcx>, + context: PlaceContext, + location: Location, + ) { + self.super_projection_elem(base, proj_base, elem, context, location); + } + + fn super_place( + &mut self, + place: &Place<'tcx>, + context: PlaceContext, + location: Location, + ) { + let mut context = context; + + if !place.projection.is_empty() { + context = if context.is_mutating_use() { + PlaceContext::MutatingUse(MutatingUseContext::Projection) + } else { + PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) + }; + } + + self.visit_place_base(&place.base, context, location); + + self.visit_projection(&place.base, + &place.projection, + context, + location); + } + + fn super_projection( + &mut self, + base: &PlaceBase<'tcx>, + projection: &[PlaceElem<'tcx>], + context: PlaceContext, + location: Location, + ) { + let mut cursor = projection; + while let [proj_base @ .., elem] = cursor { + cursor = proj_base; + self.visit_projection_elem(base, cursor, elem, context, location); + } + } + + fn super_projection_elem( + &mut self, + _base: &PlaceBase<'tcx>, + _proj_base: &[PlaceElem<'tcx>], + elem: &PlaceElem<'tcx>, + _context: PlaceContext, + location: Location, + ) { + match elem { + ProjectionElem::Field(_field, ty) => { + self.visit_ty(ty, TyContext::Location(location)); + } + ProjectionElem::Index(local) => { + self.visit_local( + local, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), + location + ); + } + ProjectionElem::Deref | + ProjectionElem::Subslice { from: _, to: _ } | + ProjectionElem::ConstantIndex { offset: _, + min_length: _, + from_end: _ } | + ProjectionElem::Downcast(_, _) => { + } + } + } + ); +} + make_mir_visitor!(Visitor,); make_mir_visitor!(MutVisitor,mut); @@ -890,7 +951,7 @@ /// Extra information passed to `visit_ty` and friends to give context /// about where the type etc appears. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Debug)] pub enum TyContext { LocalDecl { /// The index of the local variable we are visiting.
diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index 252e49d..86ed241 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs
@@ -15,7 +15,7 @@ }; use std::borrow::Cow; -use syntax_pos::symbol::InternedString; +use syntax_pos::symbol::Symbol; // Each of these queries corresponds to a function pointer field in the // `Providers` struct for requesting a value of that type, and a method @@ -61,7 +61,7 @@ /// predicate gets in the way of some checks, which are intended /// to operate over only the actual where-clauses written by the /// user.) - query predicates_of(key: DefId) -> &'tcx ty::GenericPredicates<'tcx> { + query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { cache_on_disk_if { key.is_local() } } @@ -94,6 +94,7 @@ /// of the MIR qualify_consts pass. The actual meaning of /// the value isn't known except to the pass itself. query mir_const_qualif(key: DefId) -> (u8, &'tcx BitSet<mir::Local>) { + desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } } @@ -132,7 +133,7 @@ cache_on_disk_if { key.is_local() } load_cached(tcx, id) { let promoted: Option< - rustc_data_structures::indexed_vec::IndexVec< + rustc_index::vec::IndexVec< crate::mir::Promoted, crate::mir::Body<'tcx> >> = tcx.queries.on_disk_cache.try_load_query_result(tcx, id); @@ -183,16 +184,14 @@ /// predicates (where-clauses) directly defined on it. This is /// equal to the `explicit_predicates_of` predicates plus the /// `inferred_outlives_of` predicates. - query predicates_defined_on(_: DefId) - -> &'tcx ty::GenericPredicates<'tcx> {} + query predicates_defined_on(_: DefId) -> ty::GenericPredicates<'tcx> {} /// Returns the predicates written explicitly by the user. - query explicit_predicates_of(_: DefId) - -> &'tcx ty::GenericPredicates<'tcx> {} + query explicit_predicates_of(_: DefId) -> ty::GenericPredicates<'tcx> {} /// Returns the inferred outlives predicates (e.g., for `struct /// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`). - query inferred_outlives_of(_: DefId) -> &'tcx [ty::Predicate<'tcx>] {} + query inferred_outlives_of(_: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] {} /// Maps from the `DefId` of a trait to the list of /// super-predicates. This is a subset of the full list of @@ -200,14 +199,13 @@ /// evaluate them even during type conversion, often before the /// full predicates are available (note that supertraits have /// additional acyclicity requirements). - query super_predicates_of(key: DefId) -> &'tcx ty::GenericPredicates<'tcx> { + query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing the supertraits of `{}`", tcx.def_path_str(key) } } /// To avoid cycles within the predicates of a single item we compute /// per-type-parameter predicates for resolving `T::AssocTy`. - query type_param_predicates(key: (DefId, DefId)) - -> &'tcx ty::GenericPredicates<'tcx> { + query type_param_predicates(key: (DefId, DefId)) -> ty::GenericPredicates<'tcx> { no_force desc { |tcx| "computing the bounds for type parameter `{}`", { let id = tcx.hir().as_local_hir_id(key.1).unwrap(); @@ -230,6 +228,12 @@ cycle_delay_bug } + query trivial_dropck_outlives(ty: Ty<'tcx>) -> bool { + anon + no_force + desc { "checking if `{:?}` has trivial dropck", ty } + } + query adt_dtorck_constraint( _: DefId ) -> Result<DtorckConstraint<'tcx>, NoSolution> {} @@ -290,7 +294,7 @@ query associated_item(_: DefId) -> ty::AssocItem {} query impl_trait_ref(_: DefId) -> Option<ty::TraitRef<'tcx>> {} - query impl_polarity(_: DefId) -> hir::ImplPolarity {} + query impl_polarity(_: DefId) -> ty::ImplPolarity {} query issue33140_self_ty(_: DefId) -> Option<ty::Ty<'tcx>> {} } @@ -397,10 +401,6 @@ } BorrowChecking { - query borrowck(key: DefId) -> &'tcx BorrowCheckResult { - cache_on_disk_if { key.is_local() } - } - /// Borrow-checks the function body. If this is a closure, returns /// additional requirements that the closure's creator must verify. query mir_borrowck(key: DefId) -> mir::BorrowCheckResult<'tcx> { @@ -462,14 +462,18 @@ query const_field( key: ty::ParamEnvAnd<'tcx, (&'tcx ty::Const<'tcx>, mir::Field)> ) -> &'tcx ty::Const<'tcx> { - eval_always no_force desc { "extract field of const" } } + + query const_caller_location(key: (syntax_pos::Symbol, u32, u32)) -> &'tcx ty::Const<'tcx> { + no_force + desc { "get a &core::panic::Location referring to a span" } + } } TypeChecking { - query check_match(key: DefId) -> SignalledError { + query check_match(key: DefId) { cache_on_disk_if { key.is_local() } } @@ -534,19 +538,6 @@ TypeChecking { query trait_of_item(_: DefId) -> Option<DefId> {} - query const_is_rvalue_promotable_to_static(key: DefId) -> bool { - desc { |tcx| - "const checking if rvalue is promotable to static `{}`", - tcx.def_path_str(key) - } - cache_on_disk_if { true } - } - query rvalue_promotable_map(key: DefId) -> &'tcx ItemLocalSet { - desc { |tcx| - "checking which parts of `{}` are promotable to static", - tcx.def_path_str(key) - } - } } Codegen { @@ -634,6 +625,12 @@ -> &'tcx [(CrateNum, LinkagePreference)] { desc { "dylib dependency formats of crate" } } + + query dependency_formats(_: CrateNum) + -> Lrc<crate::middle::dependency_format::Dependencies> + { + desc { "get the linkage format of all dependencies" } + } } Codegen { @@ -757,6 +754,10 @@ eval_always desc { "looking up the hash a crate" } } + query crate_host_hash(_: CrateNum) -> Option<Svh> { + eval_always + desc { "looking up the hash of a host version of a crate" } + } query original_crate_name(_: CrateNum) -> Symbol { eval_always desc { "looking up the original name a crate" } @@ -931,7 +932,7 @@ desc { "collect_and_partition_mono_items" } } query is_codegened_item(_: DefId) -> bool {} - query codegen_unit(_: InternedString) -> Arc<CodegenUnit<'tcx>> { + query codegen_unit(_: Symbol) -> Arc<CodegenUnit<'tcx>> { no_force desc { "codegen_unit" } }
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 5eda3df..2bcddea 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs
@@ -7,20 +7,16 @@ use crate::session::search_paths::SearchPath; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; use rustc_target::spec::{Target, TargetTriple}; use syntax; -use syntax::ast::{self, IntTy, UintTy, MetaItemKind}; +use syntax::ast::{self, IntTy, UintTy}; use syntax::source_map::{FileName, FilePathMapping}; use syntax::edition::{Edition, EDITION_NAME_LIST, DEFAULT_EDITION}; -use syntax::parse::{ParseSess, new_parser_from_source_str}; -use syntax::parse::token; use syntax::symbol::{sym, Symbol}; use syntax::feature_gate::UnstableFeatures; -use syntax::source_map::SourceMap; use errors::emitter::HumanReadableErrorType; use errors::{ColorConfig, FatalError, Handler}; @@ -66,7 +62,7 @@ /// This is what the `LtoCli` values get mapped to after resolving defaults and /// and taking other command line options into account. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] +#[derive(Clone, PartialEq)] pub enum Lto { /// Don't do any LTO whatsoever No, @@ -300,10 +296,10 @@ /// Use tree-based collections to cheaply get a deterministic `Hash` implementation. /// *Do not* switch `BTreeMap` or `BTreeSet` out for an unsorted container type! That /// would break dependency tracking for command-line arguments. -#[derive(Clone, Hash)] +#[derive(Clone)] pub struct Externs(BTreeMap<String, ExternEntry>); -#[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct ExternEntry { pub locations: BTreeSet<Option<String>>, pub is_private_dep: bool @@ -463,7 +459,7 @@ NativeStaticLibs, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone)] pub enum BorrowckMode { Mir, Migrate, @@ -478,14 +474,6 @@ BorrowckMode::Migrate => true, } } - - /// Returns whether we should emit the AST-based borrow checker errors. - pub fn use_ast(self) -> bool { - match self { - BorrowckMode::Mir => false, - BorrowckMode::Migrate => false, - } - } } pub enum Input { @@ -687,7 +675,7 @@ impl_stable_hash_via_hash!(EntryFnType); -#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug)] +#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, HashStable)] pub enum CrateType { Executable, Dylib, @@ -813,6 +801,7 @@ pub const parse_list: Option<&str> = Some("a space-separated list of strings"); pub const parse_opt_list: Option<&str> = Some("a space-separated list of strings"); pub const parse_opt_comma_list: Option<&str> = Some("a comma-separated list of strings"); + pub const parse_threads: Option<&str> = Some("a number"); pub const parse_uint: Option<&str> = Some("a number"); pub const parse_passes: Option<&str> = Some("a space-separated list of passes, or `all`"); @@ -956,6 +945,14 @@ } } + fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool { + match v.and_then(|s| s.parse().ok()) { + Some(0) => { *slot = ::num_cpus::get(); true }, + Some(i) => { *slot = i; true }, + None => false + } + } + fn parse_uint(slot: &mut usize, v: Option<&str>) -> bool { match v.and_then(|s| s.parse().ok()) { Some(i) => { *slot = i; true }, @@ -1152,7 +1149,8 @@ target_cpu: Option<String> = (None, parse_opt_string, [TRACKED], "select target processor (`rustc --print target-cpus` for details)"), target_feature: String = (String::new(), parse_string, [TRACKED], - "target specific attributes (`rustc --print target-features` for details)"), + "target specific attributes. (`rustc --print target-features` for details). \ + This feature is unsafe."), passes: Vec<String> = (Vec::new(), parse_list, [TRACKED], "a list of extra LLVM passes to run (space separated)"), llvm_args: Vec<String> = (Vec::new(), parse_list, [TRACKED], @@ -1259,7 +1257,11 @@ "prints the LLVM optimization passes being run"), ast_json: bool = (false, parse_bool, [UNTRACKED], "print the AST as JSON and halt"), - threads: Option<usize> = (None, parse_opt_uint, [UNTRACKED], + // We default to 1 here since we want to behave like + // a sequential compiler for now. This'll likely be adjusted + // in the future. Note that -Zthreads=0 is the way to get + // the num_cpus behavior. + threads: usize = (1, parse_threads, [UNTRACKED], "use a thread pool with N threads"), ast_json_noexpand: bool = (false, parse_bool, [UNTRACKED], "print the pre-expansion AST as JSON and halt"), @@ -1268,14 +1270,6 @@ save_analysis: bool = (false, parse_bool, [UNTRACKED], "write syntax and type analysis (in JSON format) information, in \ addition to normal output"), - flowgraph_print_loans: bool = (false, parse_bool, [UNTRACKED], - "include loan analysis data in -Z unpretty flowgraph output"), - flowgraph_print_moves: bool = (false, parse_bool, [UNTRACKED], - "include move analysis data in -Z unpretty flowgraph output"), - flowgraph_print_assigns: bool = (false, parse_bool, [UNTRACKED], - "include assignment analysis data in -Z unpretty flowgraph output"), - flowgraph_print_all: bool = (false, parse_bool, [UNTRACKED], - "include all dataflow analysis data in -Z unpretty flowgraph output"), print_region_graph: bool = (false, parse_bool, [UNTRACKED], "prints region inference graph. \ Use with RUST_REGION_GRAPH=help for more info"), @@ -1295,6 +1289,8 @@ "show extended diagnostic help"), terminal_width: Option<usize> = (None, parse_opt_uint, [UNTRACKED], "set the current terminal width"), + panic_abort_tests: bool = (false, parse_bool, [TRACKED], + "support compiling tests with panic=abort"), continue_parse_after_error: bool = (false, parse_bool, [TRACKED], "attempt to recover from parse errors (experimental)"), dep_tasks: bool = (false, parse_bool, [UNTRACKED], @@ -1317,10 +1313,6 @@ "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"), query_dep_graph: bool = (false, parse_bool, [UNTRACKED], "enable queries of the dependency graph for regression testing"), - profile_queries: bool = (false, parse_bool, [UNTRACKED], - "trace and profile the queries of the incremental compilation framework"), - profile_queries_and_keys: bool = (false, parse_bool, [UNTRACKED], - "trace and profile the queries and keys of the incremental compilation framework"), no_analysis: bool = (false, parse_bool, [UNTRACKED], "parse and expand the source, but run no analysis"), extra_plugins: Vec<String> = (Vec::new(), parse_list, [TRACKED], @@ -1346,7 +1338,7 @@ mir_opt_level: usize = (1, parse_uint, [TRACKED], "set the MIR optimization level (0-3, default: 1)"), mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED], - "emit noalias metadata for mutable references (default: yes on LLVM >= 6)"), + "emit noalias metadata for mutable references (default: no)"), dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED], "dump MIR state to file. `val` is used to select which passes and functions to dump. For example: @@ -1375,6 +1367,8 @@ "describes how to render the `rendered` field of json diagnostics"), unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], "take the breaks off const evaluation. NOTE: this is unsound"), + suppress_const_validation_back_compat_ice: bool = (false, parse_bool, [TRACKED], + "silence ICE triggered when the new const validator disagrees with the old"), osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], "pass `-install_name @rpath/...` to the macOS linker"), sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [TRACKED], @@ -1424,8 +1418,6 @@ valid types are any of the types for `--pretty`, as well as: `expanded`, `expanded,identified`, `expanded,hygiene` (with internal representations), - `flowgraph=<nodeid>` (graphviz formatted flowgraph for node), - `flowgraph,unlabelled=<nodeid>` (unlabelled graphviz formatted flowgraph for node), `everybody_loops` (all function bodies replaced with `loop {}`), `hir` (the HIR), `hir,identified`, `hir,typed` (HIR with types for each node), @@ -1472,9 +1464,12 @@ "which mangling version to use for symbol names"), binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], "include artifacts (sysroot, crate dependencies) used during compilation in dep-info"), + insert_sideeffect: bool = (false, parse_bool, [TRACKED], + "fix undefined behavior when a thread doesn't eventually make progress \ + (such as entering an empty infinite loop) by inserting llvm.sideeffect"), } -pub fn default_lib_output() -> CrateType { +pub const fn default_lib_output() -> CrateType { CrateType::Rlib } @@ -1515,22 +1510,25 @@ } for &i in &[8, 16, 32, 64, 128] { if i >= min_atomic_width && i <= max_atomic_width { - let s = i.to_string(); - ret.insert(( - sym::target_has_atomic, - Some(Symbol::intern(&s)), - )); - if &s == wordsz { + let mut insert_atomic = |s| { ret.insert(( - sym::target_has_atomic, - Some(Symbol::intern("ptr")), + sym::target_has_atomic_load_store, + Some(Symbol::intern(s)), )); + if atomic_cas { + ret.insert(( + sym::target_has_atomic, + Some(Symbol::intern(s)) + )); + } + }; + let s = i.to_string(); + insert_atomic(&s); + if &s == wordsz { + insert_atomic("ptr"); } } } - if atomic_cas { - ret.insert((sym::target_has_atomic, Some(Symbol::intern("cas")))); - } if sess.opts.debug_assertions { ret.insert((Symbol::intern("debug_assertions"), None)); } @@ -1852,58 +1850,6 @@ opts } -struct NullEmitter; - -impl errors::emitter::Emitter for NullEmitter { - fn emit_diagnostic(&mut self, _: &errors::Diagnostic) {} -} - -// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`. -pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> { - syntax::with_default_globals(move || { - let cfg = cfgspecs.into_iter().map(|s| { - - let cm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let handler = Handler::with_emitter(false, None, Box::new(NullEmitter)); - let sess = ParseSess::with_span_handler(handler, cm); - let filename = FileName::cfg_spec_source_code(&s); - let mut parser = new_parser_from_source_str(&sess, filename, s.to_string()); - - macro_rules! error {($reason: expr) => { - early_error(ErrorOutputType::default(), - &format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s)); - }} - - match &mut parser.parse_meta_item() { - Ok(meta_item) if parser.token == token::Eof => { - if meta_item.path.segments.len() != 1 { - error!("argument key must be an identifier"); - } - match &meta_item.node { - MetaItemKind::List(..) => { - error!(r#"expected `key` or `key="value"`"#); - } - MetaItemKind::NameValue(lit) if !lit.node.is_str() => { - error!("argument value must be a string"); - } - MetaItemKind::NameValue(..) | MetaItemKind::Word => { - let ident = meta_item.ident().expect("multi-segment cfg key"); - return (ident.name, meta_item.value_str()); - } - } - } - Ok(..) => {} - Err(err) => err.cancel(), - } - - error!(r#"expected `key` or `key="value"`"#); - }).collect::<ast::CrateConfig>(); - cfg.into_iter().map(|(a, b)| { - (a.to_string(), b.map(|b| b.to_string())) - }).collect() - }) -} - pub fn get_cmd_lint_options(matches: &getopts::Matches, error_format: ErrorOutputType) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) { @@ -2037,11 +1983,7 @@ return error_format; } -pub fn build_session_options_and_crate_config( - matches: &getopts::Matches, -) -> (Options, FxHashSet<(String, Option<String>)>) { - let color = parse_color(matches); - +fn parse_crate_edition(matches: &getopts::Matches) -> Edition { let edition = match matches.opt_str("edition") { Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| early_error( @@ -2068,19 +2010,14 @@ ) } - let (json_rendered, json_artifact_notifications) = parse_json(matches); + edition +} - let error_format = parse_error_format(matches, color, json_rendered); - - let unparsed_crate_types = matches.opt_strs("crate-type"); - let crate_types = parse_crate_types_from_list(unparsed_crate_types) - .unwrap_or_else(|e| early_error(error_format, &e[..])); - - - let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); - - let mut debugging_opts = build_debugging_options(matches, error_format); - +fn check_debug_option_stability( + debugging_opts: &DebuggingOptions, + error_format: ErrorOutputType, + json_rendered: HumanReadableErrorType, +) { if !debugging_opts.unstable_options { if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format { early_error( @@ -2096,7 +2033,13 @@ ); } } +} +fn parse_output_types( + debugging_opts: &DebuggingOptions, + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> OutputTypes { let mut output_types = BTreeMap::new(); if !debugging_opts.parse_only { for list in matches.opt_strs("emit") { @@ -2121,14 +2064,19 @@ if output_types.is_empty() { output_types.insert(OutputType::Exe, None); } + OutputTypes(output_types) +} - let mut cg = build_codegen_options(matches, error_format); - let mut codegen_units = cg.codegen_units; +fn should_override_cgus_and_disable_thinlto( + output_types: &OutputTypes, + matches: &getopts::Matches, + error_format: ErrorOutputType, + mut codegen_units: Option<usize>, +) -> (bool, Option<usize>) { let mut disable_thinlto = false; - // Issue #30063: if user requests LLVM-related output to one // particular path, disable codegen-units. - let incompatible: Vec<_> = output_types + let incompatible: Vec<_> = output_types.0 .iter() .map(|ot_path| ot_path.0) .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file()) @@ -2160,20 +2108,6 @@ } } - if debugging_opts.threads == Some(0) { - early_error( - error_format, - "value for threads must be a positive non-zero integer", - ); - } - - if debugging_opts.threads.unwrap_or(1) > 1 && debugging_opts.fuel.is_some() { - early_error( - error_format, - "optimization fuel is incompatible with multiple threads", - ); - } - if codegen_units == Some(0) { early_error( error_format, @@ -2181,8 +2115,32 @@ ); } - let incremental = match (&debugging_opts.incremental, &cg.incremental) { - (&Some(ref path1), &Some(ref path2)) => { + (disable_thinlto, codegen_units) +} + +fn check_thread_count(debugging_opts: &DebuggingOptions, error_format: ErrorOutputType) { + if debugging_opts.threads == 0 { + early_error( + error_format, + "value for threads must be a positive non-zero integer", + ); + } + + if debugging_opts.threads > 1 && debugging_opts.fuel.is_some() { + early_error( + error_format, + "optimization fuel is incompatible with multiple threads", + ); + } +} + +fn select_incremental_path( + debugging_opts: &DebuggingOptions, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> Option<PathBuf> { + match (&debugging_opts.incremental, &cg.incremental) { + (Some(path1), Some(path2)) => { if path1 != path2 { early_error( error_format, @@ -2196,25 +2154,19 @@ Some(path1) } } - (&Some(ref path), &None) => Some(path), - (&None, &Some(ref path)) => Some(path), - (&None, &None) => None, - }.map(|m| PathBuf::from(m)); + (Some(path), None) => Some(path), + (None, Some(path)) => Some(path), + (None, None) => None, + }.map(|m| PathBuf::from(m)) +} - if debugging_opts.profile && incremental.is_some() { - early_error( - error_format, - "can't instrument with gcov profiling when compiling incrementally", - ); - } - - if cg.profile_generate.enabled() && cg.profile_use.is_some() { - early_error( - error_format, - "options `-C profile-generate` and `-C profile-use` are exclusive", - ); - } - +fn collect_print_requests( + cg: &mut CodegenOptions, + dopts: &mut DebuggingOptions, + matches: &getopts::Matches, + is_unstable_enabled: bool, + error_format: ErrorOutputType, +) -> Vec<PrintRequest> { let mut prints = Vec::<PrintRequest>::new(); if cg.target_cpu.as_ref().map_or(false, |s| s == "help") { prints.push(PrintRequest::TargetCPUs); @@ -2232,72 +2184,105 @@ prints.push(PrintRequest::CodeModels); cg.code_model = None; } - if debugging_opts + if dopts .tls_model .as_ref() .map_or(false, |s| s == "help") { prints.push(PrintRequest::TlsModels); - debugging_opts.tls_model = None; + dopts.tls_model = None; } - let cg = cg; + prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s { + "crate-name" => PrintRequest::CrateName, + "file-names" => PrintRequest::FileNames, + "sysroot" => PrintRequest::Sysroot, + "cfg" => PrintRequest::Cfg, + "target-list" => PrintRequest::TargetList, + "target-cpus" => PrintRequest::TargetCPUs, + "target-features" => PrintRequest::TargetFeatures, + "relocation-models" => PrintRequest::RelocationModels, + "code-models" => PrintRequest::CodeModels, + "tls-models" => PrintRequest::TlsModels, + "native-static-libs" => PrintRequest::NativeStaticLibs, + "target-spec-json" => { + if is_unstable_enabled { + PrintRequest::TargetSpec + } else { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to \ + enable the target-spec-json print option", + ); + } + } + req => early_error(error_format, &format!("unknown print request `{}`", req)), + })); - let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); - let target_triple = if let Some(target) = matches.opt_str("target") { - if target.ends_with(".json") { + prints +} + +fn parse_target_triple(matches: &getopts::Matches, error_format: ErrorOutputType) -> TargetTriple { + match matches.opt_str("target") { + Some(target) if target.ends_with(".json") => { let path = Path::new(&target); TargetTriple::from_path(&path).unwrap_or_else(|_| early_error(error_format, &format!("target file {:?} does not exist", path))) - } else { - TargetTriple::TargetTriple(target) } + Some(target) => TargetTriple::TargetTriple(target), + _ => TargetTriple::from_triple(host_triple()), + } +} + +fn parse_opt_level( + matches: &getopts::Matches, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> OptLevel { + // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able + // to use them interchangeably. However, because they're technically different flags, + // we need to work out manually which should take precedence if both are supplied (i.e. + // the rightmost flag). We do this by finding the (rightmost) position of both flags and + // comparing them. Note that if a flag is not found, its position will be `None`, which + // always compared less than `Some(_)`. + let max_o = matches.opt_positions("O").into_iter().max(); + let max_c = matches.opt_strs_pos("C").into_iter().flat_map(|(i, s)| { + if let Some("opt-level") = s.splitn(2, '=').next() { + Some(i) + } else { + None + } + }).max(); + if max_o > max_c { + OptLevel::Default } else { - TargetTriple::from_triple(host_triple()) - }; - let opt_level = { - // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able - // to use them interchangeably. However, because they're technically different flags, - // we need to work out manually which should take precedence if both are supplied (i.e. - // the rightmost flag). We do this by finding the (rightmost) position of both flags and - // comparing them. Note that if a flag is not found, its position will be `None`, which - // always compared less than `Some(_)`. - let max_o = matches.opt_positions("O").into_iter().max(); - let max_c = matches.opt_strs_pos("C").into_iter().flat_map(|(i, s)| { - if let Some("opt-level") = s.splitn(2, '=').next() { - Some(i) - } else { - None - } - }).max(); - if max_o > max_c { - OptLevel::Default - } else { - match cg.opt_level.as_ref().map(String::as_ref) { - None => OptLevel::No, - Some("0") => OptLevel::No, - Some("1") => OptLevel::Less, - Some("2") => OptLevel::Default, - Some("3") => OptLevel::Aggressive, - Some("s") => OptLevel::Size, - Some("z") => OptLevel::SizeMin, - Some(arg) => { - early_error( - error_format, - &format!( - "optimization level needs to be \ - between 0-3, s or z (instead was `{}`)", - arg - ), - ); - } + match cg.opt_level.as_ref().map(String::as_ref) { + None => OptLevel::No, + Some("0") => OptLevel::No, + Some("1") => OptLevel::Less, + Some("2") => OptLevel::Default, + Some("3") => OptLevel::Aggressive, + Some("s") => OptLevel::Size, + Some("z") => OptLevel::SizeMin, + Some(arg) => { + early_error( + error_format, + &format!( + "optimization level needs to be \ + between 0-3, s or z (instead was `{}`)", + arg + ), + ); } } - }; - // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able - // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) - // for more details. - let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No); + } +} + +fn select_debuginfo( + matches: &getopts::Matches, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> DebugInfo { let max_g = matches.opt_positions("g").into_iter().max(); let max_c = matches.opt_strs_pos("C").into_iter().flat_map(|(i, s)| { if let Some("debuginfo") = s.splitn(2, '=').next() { @@ -2306,7 +2291,7 @@ None } }).max(); - let debuginfo = if max_g > max_c { + if max_g > max_c { DebugInfo::Full } else { match cg.debuginfo { @@ -2324,14 +2309,14 @@ ); } } - }; - - let mut search_paths = vec![]; - for s in &matches.opt_strs("L") { - search_paths.push(SearchPath::from_cli_opt(&s[..], error_format)); } +} - let libs = matches +fn parse_libs( + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> Vec<(String, Option<String>, Option<cstore::NativeLibraryKind>)> { + matches .opt_strs("l") .into_iter() .map(|s| { @@ -2370,52 +2355,23 @@ let new_name = name_parts.next(); (name.to_owned(), new_name.map(|n| n.to_owned()), kind) }) - .collect(); + .collect() +} - let cfg = parse_cfgspecs(matches.opt_strs("cfg")); - let test = matches.opt_present("test"); - - let is_unstable_enabled = nightly_options::is_unstable_enabled(matches); - - prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s { - "crate-name" => PrintRequest::CrateName, - "file-names" => PrintRequest::FileNames, - "sysroot" => PrintRequest::Sysroot, - "cfg" => PrintRequest::Cfg, - "target-list" => PrintRequest::TargetList, - "target-cpus" => PrintRequest::TargetCPUs, - "target-features" => PrintRequest::TargetFeatures, - "relocation-models" => PrintRequest::RelocationModels, - "code-models" => PrintRequest::CodeModels, - "tls-models" => PrintRequest::TlsModels, - "native-static-libs" => PrintRequest::NativeStaticLibs, - "target-spec-json" => { - if is_unstable_enabled { - PrintRequest::TargetSpec - } else { - early_error( - error_format, - "the `-Z unstable-options` flag must also be passed to \ - enable the target-spec-json print option", - ); - } - } - req => early_error(error_format, &format!("unknown print request `{}`", req)), - })); - - let borrowck_mode = match debugging_opts.borrowck.as_ref().map(|s| &s[..]) { +fn parse_borrowck_mode(dopts: &DebuggingOptions, error_format: ErrorOutputType) -> BorrowckMode { + match dopts.borrowck.as_ref().map(|s| &s[..]) { None | Some("migrate") => BorrowckMode::Migrate, Some("mir") => BorrowckMode::Mir, Some(m) => early_error(error_format, &format!("unknown borrowck mode `{}`", m)), - }; - - if !cg.remark.is_empty() && debuginfo == DebugInfo::None { - early_warn( - error_format, - "-C remark requires \"-C debuginfo=n\" to show source locations", - ); } +} +fn parse_externs( + matches: &getopts::Matches, + debugging_opts: &DebuggingOptions, + error_format: ErrorOutputType, + is_unstable_enabled: bool, +) -> Externs { if matches.opt_present("extern-private") && !debugging_opts.unstable_options { early_error( ErrorOutputType::default(), @@ -2456,10 +2412,14 @@ // flag entry.is_private_dep |= private; } + Externs(externs) +} - let crate_name = matches.opt_str("crate-name"); - - let remap_path_prefix = matches +fn parse_remap_path_prefix( + matches: &getopts::Matches, + error_format: ErrorOutputType +) -> Vec<(PathBuf, PathBuf)> { + matches .opt_strs("remap-path-prefix") .into_iter() .map(|remap| { @@ -2474,42 +2434,130 @@ ), } }) - .collect(); + .collect() +} - ( - Options { - crate_types, - optimize: opt_level, - debuginfo, - lint_opts, - lint_cap, - describe_lints, - output_types: OutputTypes(output_types), - search_paths, - maybe_sysroot: sysroot_opt, - target_triple, - test, - incremental, - debugging_opts, - prints, - borrowck_mode, - cg, +pub fn build_session_options(matches: &getopts::Matches) -> Options { + let color = parse_color(matches); + + let edition = parse_crate_edition(matches); + + let (json_rendered, json_artifact_notifications) = parse_json(matches); + + let error_format = parse_error_format(matches, color, json_rendered); + + let unparsed_crate_types = matches.opt_strs("crate-type"); + let crate_types = parse_crate_types_from_list(unparsed_crate_types) + .unwrap_or_else(|e| early_error(error_format, &e[..])); + + let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); + + let mut debugging_opts = build_debugging_options(matches, error_format); + check_debug_option_stability(&debugging_opts, error_format, json_rendered); + + let output_types = parse_output_types(&debugging_opts, matches, error_format); + + let mut cg = build_codegen_options(matches, error_format); + let (disable_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto( + &output_types, + matches, + error_format, + cg.codegen_units, + ); + + check_thread_count(&debugging_opts, error_format); + + let incremental = select_incremental_path(&debugging_opts, &cg, error_format); + + if debugging_opts.profile && incremental.is_some() { + early_error( error_format, - externs: Externs(externs), - crate_name, - alt_std_name: None, - libs, - unstable_features: UnstableFeatures::from_environment(), - debug_assertions, - actually_rustdoc: false, - cli_forced_codegen_units: codegen_units, - cli_forced_thinlto_off: disable_thinlto, - remap_path_prefix, - edition, - json_artifact_notifications, - }, - cfg, - ) + "can't instrument with gcov profiling when compiling incrementally", + ); + } + + if cg.profile_generate.enabled() && cg.profile_use.is_some() { + early_error( + error_format, + "options `-C profile-generate` and `-C profile-use` are exclusive", + ); + } + + let is_unstable_enabled = nightly_options::is_unstable_enabled(matches); + let prints = collect_print_requests( + &mut cg, + &mut debugging_opts, + matches, + is_unstable_enabled, + error_format, + ); + + let cg = cg; + + let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); + let target_triple = parse_target_triple(matches, error_format); + let opt_level = parse_opt_level(matches, &cg, error_format); + // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able + // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) + // for more details. + let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No); + let debuginfo = select_debuginfo(matches, &cg, error_format); + + let mut search_paths = vec![]; + for s in &matches.opt_strs("L") { + search_paths.push(SearchPath::from_cli_opt(&s[..], error_format)); + } + + let libs = parse_libs(matches, error_format); + + let test = matches.opt_present("test"); + + let borrowck_mode = parse_borrowck_mode(&debugging_opts, error_format); + + if !cg.remark.is_empty() && debuginfo == DebugInfo::None { + early_warn( + error_format, + "-C remark requires \"-C debuginfo=n\" to show source locations", + ); + } + + let externs = parse_externs(matches, &debugging_opts, error_format, is_unstable_enabled); + + let crate_name = matches.opt_str("crate-name"); + + let remap_path_prefix = parse_remap_path_prefix(matches, error_format); + + Options { + crate_types, + optimize: opt_level, + debuginfo, + lint_opts, + lint_cap, + describe_lints, + output_types, + search_paths, + maybe_sysroot: sysroot_opt, + target_triple, + test, + incremental, + debugging_opts, + prints, + borrowck_mode, + cg, + error_format, + externs, + crate_name, + alt_std_name: None, + libs, + unstable_features: UnstableFeatures::from_environment(), + debug_assertions, + actually_rustdoc: false, + cli_forced_codegen_units: codegen_units, + cli_forced_thinlto_off: disable_thinlto, + remap_path_prefix, + edition, + json_artifact_notifications, + } } pub fn make_crate_type_option() -> RustcOptGroup { @@ -2772,6 +2820,3 @@ } } } - -#[cfg(test)] -mod tests;
diff --git a/src/librustc/session/config/tests.rs b/src/librustc/session/config/tests.rs deleted file mode 100644 index 9eb6805..0000000 --- a/src/librustc/session/config/tests.rs +++ /dev/null
@@ -1,706 +0,0 @@ -use getopts; -use crate::lint; -use crate::middle::cstore; -use crate::session::config::{ - build_configuration, - build_session_options_and_crate_config, - to_crate_config -}; -use crate::session::config::{LtoCli, LinkerPluginLto, SwitchWithOptPath, ExternEntry}; -use crate::session::build_session; -use crate::session::search_paths::SearchPath; -use std::collections::{BTreeMap, BTreeSet}; -use std::iter::FromIterator; -use std::path::PathBuf; -use super::{Externs, OutputType, OutputTypes, SymbolManglingVersion}; -use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel}; -use syntax::symbol::sym; -use syntax::edition::{Edition, DEFAULT_EDITION}; -use syntax; -use super::Options; - -impl ExternEntry { - fn new_public<S: Into<String>, - I: IntoIterator<Item = Option<S>>>(locations: I) -> ExternEntry { - let locations: BTreeSet<_> = locations.into_iter().map(|o| o.map(|s| s.into())) - .collect(); - - ExternEntry { - locations, - is_private_dep: false - } - } -} - -fn optgroups() -> getopts::Options { - let mut opts = getopts::Options::new(); - for group in super::rustc_optgroups() { - (group.apply)(&mut opts); - } - return opts; -} - -fn mk_map<K: Ord, V>(entries: Vec<(K, V)>) -> BTreeMap<K, V> { - BTreeMap::from_iter(entries.into_iter()) -} - -// When the user supplies --test we should implicitly supply --cfg test -#[test] -fn test_switch_implies_cfg_test() { - syntax::with_default_globals(|| { - let matches = &match optgroups().parse(&["--test".to_string()]) { - Ok(m) => m, - Err(f) => panic!("test_switch_implies_cfg_test: {}", f), - }; - let registry = errors::registry::Registry::new(&[]); - let (sessopts, cfg) = build_session_options_and_crate_config(matches); - let sess = build_session(sessopts, None, registry); - let cfg = build_configuration(&sess, to_crate_config(cfg)); - assert!(cfg.contains(&(sym::test, None))); - }); -} - -// When the user supplies --test and --cfg test, don't implicitly add -// another --cfg test -#[test] -fn test_switch_implies_cfg_test_unless_cfg_test() { - syntax::with_default_globals(|| { - let matches = &match optgroups().parse(&["--test".to_string(), - "--cfg=test".to_string()]) { - Ok(m) => m, - Err(f) => panic!("test_switch_implies_cfg_test_unless_cfg_test: {}", f), - }; - let registry = errors::registry::Registry::new(&[]); - let (sessopts, cfg) = build_session_options_and_crate_config(matches); - let sess = build_session(sessopts, None, registry); - let cfg = build_configuration(&sess, to_crate_config(cfg)); - let mut test_items = cfg.iter().filter(|&&(name, _)| name == sym::test); - assert!(test_items.next().is_some()); - assert!(test_items.next().is_none()); - }); -} - -#[test] -fn test_can_print_warnings() { - syntax::with_default_globals(|| { - let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap(); - let registry = errors::registry::Registry::new(&[]); - let (sessopts, _) = build_session_options_and_crate_config(&matches); - let sess = build_session(sessopts, None, registry); - assert!(!sess.diagnostic().can_emit_warnings()); - }); - - syntax::with_default_globals(|| { - let matches = optgroups() - .parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]) - .unwrap(); - let registry = errors::registry::Registry::new(&[]); - let (sessopts, _) = build_session_options_and_crate_config(&matches); - let sess = build_session(sessopts, None, registry); - assert!(sess.diagnostic().can_emit_warnings()); - }); - - syntax::with_default_globals(|| { - let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap(); - let registry = errors::registry::Registry::new(&[]); - let (sessopts, _) = build_session_options_and_crate_config(&matches); - let sess = build_session(sessopts, None, registry); - assert!(sess.diagnostic().can_emit_warnings()); - }); -} - -#[test] -fn test_output_types_tracking_hash_different_paths() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - - v1.output_types = - OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("./some/thing")))]); - v2.output_types = - OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("/some/thing")))]); - v3.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); - - assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); - assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); -} - -#[test] -fn test_output_types_tracking_hash_different_construction_order() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - - v1.output_types = OutputTypes::new(&[ - (OutputType::Exe, Some(PathBuf::from("./some/thing"))), - (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), - ]); - - v2.output_types = OutputTypes::new(&[ - (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), - (OutputType::Exe, Some(PathBuf::from("./some/thing"))), - ]); - - assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); -} - -#[test] -fn test_externs_tracking_hash_different_construction_order() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - - v1.externs = Externs::new(mk_map(vec![ - ( - String::from("a"), - ExternEntry::new_public(vec![Some("b"), Some("c")]) - ), - ( - String::from("d"), - ExternEntry::new_public(vec![Some("e"), Some("f")]) - ), - ])); - - v2.externs = Externs::new(mk_map(vec![ - ( - String::from("d"), - ExternEntry::new_public(vec![Some("e"), Some("f")]) - ), - ( - String::from("a"), - ExternEntry::new_public(vec![Some("b"), Some("c")]) - ), - ])); - - v3.externs = Externs::new(mk_map(vec![ - ( - String::from("a"), - ExternEntry::new_public(vec![Some("b"), Some("c")]) - ), - ( - String::from("d"), - ExternEntry::new_public(vec![Some("f"), Some("e")]) - ), - ])); - - assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); - assert_eq!(v1.dep_tracking_hash(), v3.dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v3.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); -} - -#[test] -fn test_lints_tracking_hash_different_values() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - - v1.lint_opts = vec![ - (String::from("a"), lint::Allow), - (String::from("b"), lint::Warn), - (String::from("c"), lint::Deny), - (String::from("d"), lint::Forbid), - ]; - - v2.lint_opts = vec![ - (String::from("a"), lint::Allow), - (String::from("b"), lint::Warn), - (String::from("X"), lint::Deny), - (String::from("d"), lint::Forbid), - ]; - - v3.lint_opts = vec![ - (String::from("a"), lint::Allow), - (String::from("b"), lint::Warn), - (String::from("c"), lint::Forbid), - (String::from("d"), lint::Deny), - ]; - - assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); - assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); -} - -#[test] -fn test_lints_tracking_hash_different_construction_order() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - - v1.lint_opts = vec![ - (String::from("a"), lint::Allow), - (String::from("b"), lint::Warn), - (String::from("c"), lint::Deny), - (String::from("d"), lint::Forbid), - ]; - - v2.lint_opts = vec![ - (String::from("a"), lint::Allow), - (String::from("c"), lint::Deny), - (String::from("b"), lint::Warn), - (String::from("d"), lint::Forbid), - ]; - - assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); -} - -#[test] -fn test_search_paths_tracking_hash_different_order() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - let mut v4 = Options::default(); - - const JSON: super::ErrorOutputType = super::ErrorOutputType::Json { - pretty: false, - json_rendered: super::HumanReadableErrorType::Default(super::ColorConfig::Never), - }; - - // Reference - v1.search_paths - .push(SearchPath::from_cli_opt("native=abc", JSON)); - v1.search_paths - .push(SearchPath::from_cli_opt("crate=def", JSON)); - v1.search_paths - .push(SearchPath::from_cli_opt("dependency=ghi", JSON)); - v1.search_paths - .push(SearchPath::from_cli_opt("framework=jkl", JSON)); - v1.search_paths - .push(SearchPath::from_cli_opt("all=mno", JSON)); - - v2.search_paths - .push(SearchPath::from_cli_opt("native=abc", JSON)); - v2.search_paths - .push(SearchPath::from_cli_opt("dependency=ghi", JSON)); - v2.search_paths - .push(SearchPath::from_cli_opt("crate=def", JSON)); - v2.search_paths - .push(SearchPath::from_cli_opt("framework=jkl", JSON)); - v2.search_paths - .push(SearchPath::from_cli_opt("all=mno", JSON)); - - v3.search_paths - .push(SearchPath::from_cli_opt("crate=def", JSON)); - v3.search_paths - .push(SearchPath::from_cli_opt("framework=jkl", JSON)); - v3.search_paths - .push(SearchPath::from_cli_opt("native=abc", JSON)); - v3.search_paths - .push(SearchPath::from_cli_opt("dependency=ghi", JSON)); - v3.search_paths - .push(SearchPath::from_cli_opt("all=mno", JSON)); - - v4.search_paths - .push(SearchPath::from_cli_opt("all=mno", JSON)); - v4.search_paths - .push(SearchPath::from_cli_opt("native=abc", JSON)); - v4.search_paths - .push(SearchPath::from_cli_opt("crate=def", JSON)); - v4.search_paths - .push(SearchPath::from_cli_opt("dependency=ghi", JSON)); - v4.search_paths - .push(SearchPath::from_cli_opt("framework=jkl", JSON)); - - assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() == v4.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); - assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash()); -} - -#[test] -fn test_native_libs_tracking_hash_different_values() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - let mut v4 = Options::default(); - - // Reference - v1.libs = vec![ - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("b"), None, Some(cstore::NativeFramework)), - (String::from("c"), None, Some(cstore::NativeUnknown)), - ]; - - // Change label - v2.libs = vec![ - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("X"), None, Some(cstore::NativeFramework)), - (String::from("c"), None, Some(cstore::NativeUnknown)), - ]; - - // Change kind - v3.libs = vec![ - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("b"), None, Some(cstore::NativeStatic)), - (String::from("c"), None, Some(cstore::NativeUnknown)), - ]; - - // Change new-name - v4.libs = vec![ - (String::from("a"), None, Some(cstore::NativeStatic)), - ( - String::from("b"), - Some(String::from("X")), - Some(cstore::NativeFramework), - ), - (String::from("c"), None, Some(cstore::NativeUnknown)), - ]; - - assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() != v4.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); - assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash()); -} - -#[test] -fn test_native_libs_tracking_hash_different_order() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - - // Reference - v1.libs = vec![ - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("b"), None, Some(cstore::NativeFramework)), - (String::from("c"), None, Some(cstore::NativeUnknown)), - ]; - - v2.libs = vec![ - (String::from("b"), None, Some(cstore::NativeFramework)), - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("c"), None, Some(cstore::NativeUnknown)), - ]; - - v3.libs = vec![ - (String::from("c"), None, Some(cstore::NativeUnknown)), - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("b"), None, Some(cstore::NativeFramework)), - ]; - - assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash()); - assert!(v2.dep_tracking_hash() == v3.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); -} - -#[test] -fn test_codegen_options_tracking_hash() { - let reference = Options::default(); - let mut opts = Options::default(); - - // Make sure the changing an [UNTRACKED] option leaves the hash unchanged - opts.cg.ar = Some(String::from("abc")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.linker = Some(PathBuf::from("linker")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.link_args = Some(vec![String::from("abc"), String::from("def")]); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.link_dead_code = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.rpath = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.extra_filename = String::from("extra-filename"); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.codegen_units = Some(42); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.remark = super::Passes::Some(vec![String::from("pass1"), String::from("pass2")]); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.save_temps = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts.cg.incremental = Some(String::from("abc")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - // Make sure changing a [TRACKED] option changes the hash - opts = reference.clone(); - opts.cg.lto = LtoCli::Fat; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.target_cpu = Some(String::from("abc")); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.target_feature = String::from("all the features, all of them"); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.passes = vec![String::from("1"), String::from("2")]; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.llvm_args = vec![String::from("1"), String::from("2")]; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.overflow_checks = Some(true); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.no_prepopulate_passes = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.no_vectorize_loops = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.no_vectorize_slp = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.soft_float = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.prefer_dynamic = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.no_integrated_as = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.no_redzone = Some(true); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.relocation_model = Some(String::from("relocation model")); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.code_model = Some(String::from("code model")); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.tls_model = Some(String::from("tls model")); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.profile_generate = SwitchWithOptPath::Enabled(None); - assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.profile_use = Some(PathBuf::from("abc")); - assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.metadata = vec![String::from("A"), String::from("B")]; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.debuginfo = Some(0xdeadbeef); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.debuginfo = Some(0xba5eba11); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.force_frame_pointers = Some(false); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.debug_assertions = Some(true); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.inline_threshold = Some(0xf007ba11); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.panic = Some(PanicStrategy::Abort); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.cg.linker_plugin_lto = LinkerPluginLto::LinkerPluginAuto; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); -} - -#[test] -fn test_debugging_options_tracking_hash() { - let reference = Options::default(); - let mut opts = Options::default(); - - // Make sure the changing an [UNTRACKED] option leaves the hash unchanged - opts.debugging_opts.verbose = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.time_passes = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.time_llvm_passes = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.input_stats = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.borrowck_stats = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.meta_stats = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.print_link_args = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.print_llvm_passes = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.ast_json = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.ast_json_noexpand = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.ls = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.save_analysis = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.flowgraph_print_loans = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.flowgraph_print_moves = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.flowgraph_print_assigns = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.flowgraph_print_all = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.print_region_graph = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.parse_only = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.incremental = Some(String::from("abc")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.dump_dep_graph = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.query_dep_graph = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.no_analysis = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.unstable_options = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.trace_macros = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.keep_hygiene_data = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.keep_ast = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.print_mono_items = Some(String::from("abc")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.dump_mir = Some(String::from("abc")); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.dump_mir_dir = String::from("abc"); - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.dump_mir_graphviz = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - - // Make sure changing a [TRACKED] option changes the hash - opts = reference.clone(); - opts.debugging_opts.asm_comments = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.verify_llvm_ir = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.no_landing_pads = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.fewer_names = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.no_codegen = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.treat_err_as_bug = Some(1); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.report_delayed_bugs = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.continue_parse_after_error = true; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.extra_plugins = vec![String::from("plugin1"), String::from("plugin2")]; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.force_overflow_checks = Some(true); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.show_span = Some(String::from("abc")); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.mir_opt_level = 3; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.relro_level = Some(RelroLevel::Full); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.merge_functions = Some(MergeFunctions::Disabled); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.allow_features = Some(vec![String::from("lang_items")]); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - - opts = reference.clone(); - opts.debugging_opts.symbol_mangling_version = SymbolManglingVersion::V0; - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); -} - -#[test] -fn test_edition_parsing() { - // test default edition - let options = Options::default(); - assert!(options.edition == DEFAULT_EDITION); - - let matches = optgroups() - .parse(&["--edition=2018".to_string()]) - .unwrap(); - let (sessopts, _) = build_session_options_and_crate_config(&matches); - assert!(sessopts.edition == Edition::Edition2018) -}
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index a24fed8..13b76b7 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs
@@ -6,17 +6,14 @@ use rustc_data_structures::fingerprint::Fingerprint; use crate::lint; -use crate::lint::builtin::BuiltinLintDiagnostics; -use crate::middle::dependency_format; -use crate::session::config::{OutputType, PrintRequest, SwitchWithOptPath}; +use crate::session::config::{OutputType, PrintRequest, Sanitizer, SwitchWithOptPath}; use crate::session::search_paths::{PathKind, SearchPath}; use crate::util::nodemap::{FxHashMap, FxHashSet}; use crate::util::common::{duration_to_secs_str, ErrorReported}; -use crate::util::common::ProfileQueriesMsg; use rustc_data_structures::base_n; use rustc_data_structures::sync::{ - self, Lrc, Lock, OneThread, Once, RwLock, AtomicU64, AtomicUsize, Ordering, + self, Lrc, Lock, OneThread, Once, AtomicU64, AtomicUsize, Ordering, Ordering::SeqCst, }; @@ -26,14 +23,14 @@ use errors::annotate_snippet_emitter_writer::{AnnotateSnippetEmitterWriter}; use syntax::ast::{self, NodeId}; use syntax::edition::Edition; -use syntax::ext::allocator::AllocatorKind; +use syntax::expand::allocator::AllocatorKind; use syntax::feature_gate::{self, AttributeType}; use syntax::json::JsonEmitter; use syntax::source_map; -use syntax::parse::{self, ParseSess}; +use syntax::sess::ParseSess; use syntax::symbol::Symbol; use syntax_pos::{MultiSpan, Span}; -use crate::util::profiling::SelfProfiler; +use crate::util::profiling::{SelfProfiler, SelfProfilerRef}; use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple}; use rustc_data_structures::flock; @@ -47,7 +44,7 @@ use std::io::Write; use std::path::PathBuf; use std::time::Duration; -use std::sync::{Arc, mpsc}; +use std::sync::Arc; mod code_stats; pub mod config; @@ -79,11 +76,6 @@ /// if the value stored here has been affected by path remapping. pub working_dir: (PathBuf, bool), - // FIXME: `lint_store` and `buffered_lints` are not thread-safe, - // but are only used in a single thread. - pub lint_store: RwLock<lint::LintStore>, - pub buffered_lints: Lock<Option<lint::LintBuffer>>, - /// Set of `(DiagnosticId, Option<Span>, message)` tuples tracking /// (sub)diagnostics that have been set once, but should not be set again, /// in order to avoid redundantly verbose output (Issue #24690, #44953). @@ -91,7 +83,6 @@ pub plugin_llvm_passes: OneThread<RefCell<Vec<String>>>, pub plugin_attributes: Lock<Vec<(Symbol, AttributeType)>>, pub crate_types: Once<Vec<config::CrateType>>, - pub dependency_formats: Once<dependency_format::Dependencies>, /// The `crate_disambiguator` is constructed out of all the `-C metadata` /// arguments passed to the compiler. Its value together with the crate-name /// forms a unique global identifier for the crate. It is used to allow @@ -127,11 +118,8 @@ /// `-Zquery-dep-graph` is specified. pub cgu_reuse_tracker: CguReuseTracker, - /// Used by `-Z profile-queries` in `util::common`. - pub profile_channel: Lock<Option<mpsc::Sender<ProfileQueriesMsg>>>, - /// Used by `-Z self-profile`. - pub self_profiling: Option<Arc<SelfProfiler>>, + pub prof: SelfProfilerRef, /// Some measurements that are being gathered during compilation. pub perf_stats: PerfStats, @@ -316,11 +304,15 @@ pub fn has_errors(&self) -> bool { self.diagnostic().has_errors() } + pub fn has_errors_or_delayed_span_bugs(&self) -> bool { + self.diagnostic().has_errors_or_delayed_span_bugs() + } pub fn abort_if_errors(&self) { self.diagnostic().abort_if_errors(); } pub fn compile_status(&self) -> Result<(), ErrorReported> { if self.has_errors() { + self.diagnostic().emit_stashed_diagnostics(); Err(ErrorReported) } else { Ok(()) @@ -366,35 +358,6 @@ self.diagnostic().span_note_without_error(sp, msg) } - pub fn buffer_lint<S: Into<MultiSpan>>( - &self, - lint: &'static lint::Lint, - id: ast::NodeId, - sp: S, - msg: &str, - ) { - match *self.buffered_lints.borrow_mut() { - Some(ref mut buffer) => { - buffer.add_lint(lint, id, sp.into(), msg, BuiltinLintDiagnostics::Normal) - } - None => bug!("can't buffer lints after HIR lowering"), - } - } - - pub fn buffer_lint_with_diagnostic<S: Into<MultiSpan>>( - &self, - lint: &'static lint::Lint, - id: ast::NodeId, - sp: S, - msg: &str, - diagnostic: BuiltinLintDiagnostics, - ) { - match *self.buffered_lints.borrow_mut() { - Some(ref mut buffer) => buffer.add_lint(lint, id, sp.into(), msg, diagnostic), - None => bug!("can't buffer lints after HIR lowering"), - } - } - pub fn reserve_node_ids(&self, count: usize) -> ast::NodeId { let id = self.next_node_id.get(); @@ -510,13 +473,6 @@ pub fn time_extended(&self) -> bool { self.opts.debugging_opts.time_passes } - pub fn profile_queries(&self) -> bool { - self.opts.debugging_opts.profile_queries - || self.opts.debugging_opts.profile_queries_and_keys - } - pub fn profile_queries_and_keys(&self) -> bool { - self.opts.debugging_opts.profile_queries_and_keys - } pub fn instrument_mcount(&self) -> bool { self.opts.debugging_opts.instrument_mcount } @@ -638,6 +594,14 @@ .output_types .contains_key(&OutputType::LlvmAssembly) || self.opts.output_types.contains_key(&OutputType::Bitcode); + + // Address sanitizer and memory sanitizer use alloca name when reporting an issue. + let more_names = match self.opts.debugging_opts.sanitizer { + Some(Sanitizer::Address) => true, + Some(Sanitizer::Memory) => true, + _ => more_names, + }; + self.opts.debugging_opts.fewer_names || !more_names } @@ -836,24 +800,6 @@ } } - #[inline(never)] - #[cold] - fn profiler_active<F: FnOnce(&SelfProfiler) -> ()>(&self, f: F) { - match &self.self_profiling { - None => bug!("profiler_active() called but there was no profiler active"), - Some(profiler) => { - f(&profiler); - } - } - } - - #[inline(always)] - pub fn profiler<F: FnOnce(&SelfProfiler) -> ()>(&self, f: F) { - if unlikely!(self.self_profiling.is_some()) { - self.profiler_active(f) - } - } - pub fn print_perf_stats(&self) { println!( "Total time spent computing symbol hashes: {}", @@ -899,14 +845,8 @@ /// Returns the number of query threads that should be used for this /// compilation - pub fn threads_from_count(query_threads: Option<usize>) -> usize { - query_threads.unwrap_or(::num_cpus::get()) - } - - /// Returns the number of query threads that should be used for this - /// compilation pub fn threads(&self) -> usize { - Self::threads_from_count(self.opts.debugging_opts.threads) + self.opts.debugging_opts.threads } /// Returns the number of codegen units that should be used for this @@ -1187,7 +1127,7 @@ ); let target_cfg = config::build_target_config(&sopts, &span_diagnostic); - let parse_sess = parse::ParseSess::with_span_handler( + let parse_sess = ParseSess::with_span_handler( span_diagnostic, source_map, ); @@ -1241,13 +1181,10 @@ sysroot, local_crate_source_file, working_dir, - lint_store: RwLock::new(lint::LintStore::new()), - buffered_lints: Lock::new(Some(Default::default())), one_time_diagnostics: Default::default(), plugin_llvm_passes: OneThread::new(RefCell::new(Vec::new())), plugin_attributes: Lock::new(Vec::new()), crate_types: Once::new(), - dependency_formats: Once::new(), crate_disambiguator: Once::new(), features: Once::new(), recursion_limit: Once::new(), @@ -1259,8 +1196,7 @@ imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())), incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), cgu_reuse_tracker, - self_profiling: self_profiler, - profile_channel: Lock::new(None), + prof: SelfProfilerRef::new(self_profiler), perf_stats: PerfStats { symbol_hash_time: Lock::new(Duration::from_secs(0)), decode_def_path_tables_time: Lock::new(Duration::from_secs(0)),
diff --git a/src/librustc/session/search_paths.rs b/src/librustc/session/search_paths.rs index 3695f0a..949dad7 100644 --- a/src/librustc/session/search_paths.rs +++ b/src/librustc/session/search_paths.rs
@@ -1,5 +1,4 @@ use std::path::{Path, PathBuf}; -use rustc_macros::HashStable; use crate::session::{early_error, config}; use crate::session::filesearch::make_target_lib_path; @@ -10,7 +9,7 @@ pub files: Vec<PathBuf>, } -#[derive(Eq, PartialEq, Clone, Copy, Debug, PartialOrd, Ord, Hash, HashStable)] +#[derive(PartialEq, Clone, Copy, Debug, HashStable)] pub enum PathKind { Native, Crate,
diff --git a/src/librustc/traits/auto_trait.rs b/src/librustc/traits/auto_trait.rs index d89cf8e..9faf58a 100644 --- a/src/librustc/traits/auto_trait.rs +++ b/src/librustc/traits/auto_trait.rs
@@ -321,7 +321,7 @@ match vtable { Vtable::VtableImpl(VtableImplData { impl_def_id, .. }) => { // Blame tidy for the weird bracket placement - if infcx.tcx.impl_polarity(*impl_def_id) == hir::ImplPolarity::Negative + if infcx.tcx.impl_polarity(*impl_def_id) == ty::ImplPolarity::Negative { debug!("evaluate_nested_obligations: Found explicit negative impl\ {:?}, bailing out", impl_def_id); @@ -601,7 +601,7 @@ } pub fn is_of_param(&self, ty: Ty<'_>) -> bool { - return match ty.sty { + return match ty.kind { ty::Param(_) => true, ty::Projection(p) => self.is_of_param(p.self_ty()), _ => false, @@ -609,7 +609,7 @@ } fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool { - match p.ty().skip_binder().sty { + match p.ty().skip_binder().kind { ty::Projection(proj) if proj == p.skip_binder().projection_ty => { true },
diff --git a/src/librustc/traits/chalk_fulfill.rs b/src/librustc/traits/chalk_fulfill.rs index a7e1f2a..d9e83df 100644 --- a/src/librustc/traits/chalk_fulfill.rs +++ b/src/librustc/traits/chalk_fulfill.rs
@@ -108,7 +108,7 @@ goal: obligation.goal.predicate, }, &mut orig_values); - match infcx.tcx.global_tcx().evaluate_goal(canonical_goal) { + match infcx.tcx.evaluate_goal(canonical_goal) { Ok(response) => { if response.is_proven() { making_progress = true;
diff --git a/src/librustc/traits/codegen/mod.rs b/src/librustc/traits/codegen/mod.rs index 97fb430..9dff699 100644 --- a/src/librustc/traits/codegen/mod.rs +++ b/src/librustc/traits/codegen/mod.rs
@@ -3,12 +3,10 @@ // seems likely that they should eventually be merged into more // general routines. -use crate::dep_graph::{DepKind, DepTrackingMapConfig}; -use std::marker::PhantomData; use crate::infer::InferCtxt; use crate::traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, TraitEngine, Vtable}; -use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{self, TyCtxt}; use crate::ty::subst::{Subst, SubstsRef}; use crate::ty::fold::TypeFoldable; @@ -100,33 +98,8 @@ } } -// Implement DepTrackingMapConfig for `trait_cache` -pub struct TraitSelectionCache<'tcx> { - data: PhantomData<&'tcx ()> -} - -impl<'tcx> DepTrackingMapConfig for TraitSelectionCache<'tcx> { - type Key = (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>); - type Value = Vtable<'tcx, ()>; - fn to_dep_kind() -> DepKind { - DepKind::TraitSelect - } -} - // # Global Cache -pub struct ProjectionCache<'tcx> { - data: PhantomData<&'tcx ()>, -} - -impl<'tcx> DepTrackingMapConfig for ProjectionCache<'tcx> { - type Key = Ty<'tcx>; - type Value = Ty<'tcx>; - fn to_dep_kind() -> DepKind { - DepKind::TraitSelect - } -} - impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Finishes processes any obligations that remain in the /// fulfillment context, and then returns the result with all type
diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs index b6f0add..49a4d17 100644 --- a/src/librustc/traits/coherence.rs +++ b/src/librustc/traits/coherence.rs
@@ -237,7 +237,7 @@ } pub enum OrphanCheckErr<'tcx> { - NoLocalInputType, + NonLocalInputType(Vec<(Ty<'tcx>, bool /* Is this the first input type? */)>), UncoveredTy(Ty<'tcx>), } @@ -355,7 +355,7 @@ /// Note that this function is never called for types that have both type /// parameters and inference variables. fn orphan_check_trait_ref<'tcx>( - tcx: TyCtxt<'_>, + tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, in_crate: InCrate, ) -> Result<(), OrphanCheckErr<'tcx>> { @@ -378,26 +378,51 @@ // Let Ti be the first such type. // - No uncovered type parameters P1..=Pn may appear in T0..Ti (excluding Ti) // - for input_ty in trait_ref.input_types() { + fn uncover_fundamental_ty<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + in_crate: InCrate, + ) -> Vec<Ty<'tcx>> { + if fundamental_ty(ty) && ty_is_non_local(tcx, ty, in_crate).is_some() { + ty.walk_shallow().flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)).collect() + } else { + vec![ty] + } + } + + let mut non_local_spans = vec![]; + for (i, input_ty) in trait_ref + .input_types() + .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) + .enumerate() + { debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty); - if ty_is_local(tcx, input_ty, in_crate) { + let non_local_tys = ty_is_non_local(tcx, input_ty, in_crate); + if non_local_tys.is_none() { debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty); return Ok(()); - } else if let ty::Param(_) = input_ty.sty { + } else if let ty::Param(_) = input_ty.kind { debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty); return Err(OrphanCheckErr::UncoveredTy(input_ty)) } + if let Some(non_local_tys) = non_local_tys { + for input_ty in non_local_tys { + non_local_spans.push((input_ty, i == 0)); + } + } } // If we exit above loop, never found a local type. debug!("orphan_check_trait_ref: no local type"); - Err(OrphanCheckErr::NoLocalInputType) + Err(OrphanCheckErr::NonLocalInputType(non_local_spans)) } else { + let mut non_local_spans = vec![]; // First, create an ordered iterator over all the type // parameters to the trait, with the self type appearing // first. Find the first input type that either references a // type parameter OR some local type. - for input_ty in trait_ref.input_types() { - if ty_is_local(tcx, input_ty, in_crate) { + for (i, input_ty) in trait_ref.input_types().enumerate() { + let non_local_tys = ty_is_non_local(tcx, input_ty, in_crate); + if non_local_tys.is_none() { debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty); // First local input type. Check that there are no @@ -424,15 +449,21 @@ debug!("orphan_check_trait_ref: uncovered type `{:?}`", param); return Err(OrphanCheckErr::UncoveredTy(param)); } + + if let Some(non_local_tys) = non_local_tys { + for input_ty in non_local_tys { + non_local_spans.push((input_ty, i == 0)); + } + } } // If we exit above loop, never found a local type. debug!("orphan_check_trait_ref: no local type"); - Err(OrphanCheckErr::NoLocalInputType) + Err(OrphanCheckErr::NonLocalInputType(non_local_spans)) } } -fn uncovered_tys<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, in_crate: InCrate) -> Vec<Ty<'tcx>> { - if ty_is_local_constructor(ty, in_crate) { +fn uncovered_tys<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, in_crate: InCrate) -> Vec<Ty<'tcx>> { + if ty_is_non_local_constructor(tcx, ty, in_crate).is_none() { vec![] } else if fundamental_ty(ty) { ty.walk_shallow() @@ -444,19 +475,33 @@ } fn is_possibly_remote_type(ty: Ty<'_>, _in_crate: InCrate) -> bool { - match ty.sty { + match ty.kind { ty::Projection(..) | ty::Param(..) => true, _ => false, } } -fn ty_is_local(tcx: TyCtxt<'_>, ty: Ty<'_>, in_crate: InCrate) -> bool { - ty_is_local_constructor(ty, in_crate) || - fundamental_ty(ty) && ty.walk_shallow().any(|t| ty_is_local(tcx, t, in_crate)) +fn ty_is_non_local<'t>(tcx: TyCtxt<'t>, ty: Ty<'t>, in_crate: InCrate) -> Option<Vec<Ty<'t>>> { + match ty_is_non_local_constructor(tcx, ty, in_crate) { + Some(ty) => if !fundamental_ty(ty) { + Some(vec![ty]) + } else { + let tys: Vec<_> = ty.walk_shallow() + .filter_map(|t| ty_is_non_local(tcx, t, in_crate)) + .flat_map(|i| i) + .collect(); + if tys.is_empty() { + None + } else { + Some(tys) + } + }, + None => None, + } } fn fundamental_ty(ty: Ty<'_>) -> bool { - match ty.sty { + match ty.kind { ty::Ref(..) => true, ty::Adt(def, _) => def.is_fundamental(), _ => false @@ -472,10 +517,14 @@ } } -fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool { - debug!("ty_is_local_constructor({:?})", ty); +fn ty_is_non_local_constructor<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + in_crate: InCrate, +) -> Option<Ty<'tcx>> { + debug!("ty_is_non_local_constructor({:?})", ty); - match ty.sty { + match ty.kind { ty::Bool | ty::Char | ty::Int(..) | @@ -492,34 +541,54 @@ ty::Tuple(..) | ty::Param(..) | ty::Projection(..) => { - false + Some(ty) } ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match in_crate { - InCrate::Local => false, + InCrate::Local => Some(ty), // The inference variable might be unified with a local // type in that remote crate. - InCrate::Remote => true, + InCrate::Remote => None, }, - ty::Adt(def, _) => def_id_is_local(def.did, in_crate), - ty::Foreign(did) => def_id_is_local(did, in_crate), + ty::Adt(def, _) => if def_id_is_local(def.did, in_crate) { + None + } else { + Some(ty) + }, + ty::Foreign(did) => if def_id_is_local(did, in_crate) { + None + } else { + Some(ty) + }, + ty::Opaque(did, _) => { + // Check the underlying type that this opaque + // type resolves to. + // This recursion will eventually terminate, + // since we've already managed to successfully + // resolve all opaque types by this point + let real_ty = tcx.type_of(did); + ty_is_non_local_constructor(tcx, real_ty, in_crate) + } ty::Dynamic(ref tt, ..) => { if let Some(principal) = tt.principal() { - def_id_is_local(principal.def_id(), in_crate) + if def_id_is_local(principal.def_id(), in_crate) { + None + } else { + Some(ty) + } } else { - false + Some(ty) } } - ty::Error => true, + ty::Error => None, ty::UnnormalizedProjection(..) | ty::Closure(..) | ty::Generator(..) | - ty::GeneratorWitness(..) | - ty::Opaque(..) => { + ty::GeneratorWitness(..) => { bug!("ty_is_local invoked on unexpected type: {:?}", ty) } }
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 7935c01..1f7bce1 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs
@@ -195,8 +195,7 @@ obligation: &PredicateObligation<'tcx>, error: &MismatchedProjectionTypes<'tcx>, ) { - let predicate = - self.resolve_vars_if_possible(&obligation.predicate); + let predicate = self.resolve_vars_if_possible(&obligation.predicate); if predicate.references_error() { return @@ -227,25 +226,45 @@ 0, &mut obligations ); + + debug!("report_projection_error obligation.cause={:?} obligation.param_env={:?}", + obligation.cause, obligation.param_env); + + debug!("report_projection_error normalized_ty={:?} data.ty={:?}", + normalized_ty, data.ty); + + let is_normalized_ty_expected = match &obligation.cause.code { + ObligationCauseCode::ItemObligation(_) | + ObligationCauseCode::BindingObligation(_, _) | + ObligationCauseCode::ObjectCastObligation(_) => false, + _ => true, + }; + if let Err(error) = self.at(&obligation.cause, obligation.param_env) - .eq(normalized_ty, data.ty) { - values = Some(infer::ValuePairs::Types(ExpectedFound { - expected: normalized_ty, - found: data.ty, - })); + .eq_exp(is_normalized_ty_expected, normalized_ty, data.ty) + { + values = Some(infer::ValuePairs::Types( + ExpectedFound::new(is_normalized_ty_expected, normalized_ty, data.ty))); + err_buf = error; err = &err_buf; } } let msg = format!("type mismatch resolving `{}`", predicate); - let error_id = (DiagnosticMessageId::ErrorId(271), - Some(obligation.cause.span), msg); + let error_id = ( + DiagnosticMessageId::ErrorId(271), + Some(obligation.cause.span), + msg, + ); let fresh = self.tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); if fresh { let mut diag = struct_span_err!( - self.tcx.sess, obligation.cause.span, E0271, - "type mismatch resolving `{}`", predicate + self.tcx.sess, + obligation.cause.span, + E0271, + "type mismatch resolving `{}`", + predicate ); self.note_type_err(&mut diag, &obligation.cause, None, values, err); self.note_obligation_cause(&mut diag, obligation); @@ -258,7 +277,7 @@ /// returns the fuzzy category of a given type, or None /// if the type can be equated to any type. fn type_category(t: Ty<'_>) -> Option<u32> { - match t.sty { + match t.kind { ty::Bool => Some(0), ty::Char => Some(1), ty::Str => Some(2), @@ -288,7 +307,7 @@ } match (type_category(a), type_category(b)) { - (Some(cat_a), Some(cat_b)) => match (&a.sty, &b.sty) { + (Some(cat_a), Some(cat_b)) => match (&a.kind, &b.kind) { (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => def_a == def_b, _ => cat_a == cat_b }, @@ -347,6 +366,52 @@ } } + fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str> { + self.tcx.hir().body(body_id).generator_kind.map(|gen_kind| { + match gen_kind { + hir::GeneratorKind::Gen => "a generator", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "an async block", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "an async function", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "an async closure", + } + }) + } + + /// Used to set on_unimplemented's `ItemContext` + /// to be the enclosing (async) block/function/closure + fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { + let hir = &self.tcx.hir(); + let node = hir.find(hir_id)?; + if let hir::Node::Item( + hir::Item{kind: hir::ItemKind::Fn(_ ,fn_header ,_ , body_id), .. }) = &node { + self.describe_generator(*body_id).or_else(|| + Some(if let hir::FnHeader{ asyncness: hir::IsAsync::Async, .. } = fn_header { + "an async function" + } else { + "a function" + }) + ) + } else if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability), .. }) = &node { + self.describe_generator(*body_id).or_else(|| + Some(if gen_movability.is_some() { + "an async closure" + } else { + "a closure" + }) + ) + } else if let hir::Node::Expr(hir::Expr { .. }) = &node { + let parent_hid = hir.get_parent_node(hir_id); + if parent_hid != hir_id { + return self.describe_enclosure(parent_hid); + } else { + None + } + } else { + None + } + } + fn on_unimplemented_note( &self, trait_ref: ty::PolyTraitRef<'tcx>, @@ -357,6 +422,9 @@ let trait_ref = *trait_ref.skip_binder(); let mut flags = vec![]; + flags.push((sym::item_context, + self.describe_enclosure(obligation.cause.body_id).map(|s|s.to_owned()))); + match obligation.cause.code { ObligationCauseCode::BuiltinDerivedObligation(..) | ObligationCauseCode::ImplDerivedObligation(..) => {} @@ -406,7 +474,7 @@ }, GenericParamDefKind::Lifetime => continue, }; - let name = param.name.as_symbol(); + let name = param.name; flags.push((name, Some(value))); } @@ -419,7 +487,7 @@ flags.push((sym::_Self, Some("{integral}".to_owned()))); } - if let ty::Array(aty, len) = self_ty.sty { + if let ty::Array(aty, len) = self_ty.kind { flags.push((sym::_Self, Some("[]".to_owned()))); flags.push((sym::_Self, Some(format!("[{}]", aty)))); if let Some(def) = aty.ty_adt_def() { @@ -453,21 +521,17 @@ } } - fn find_similar_impl_candidates(&self, - trait_ref: ty::PolyTraitRef<'tcx>) - -> Vec<ty::TraitRef<'tcx>> - { - let simp = fast_reject::simplify_type(self.tcx, - trait_ref.skip_binder().self_ty(), - true); + fn find_similar_impl_candidates( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Vec<ty::TraitRef<'tcx>> { + let simp = fast_reject::simplify_type(self.tcx, trait_ref.skip_binder().self_ty(), true); let all_impls = self.tcx.all_impls(trait_ref.def_id()); match simp { Some(simp) => all_impls.iter().filter_map(|&def_id| { let imp = self.tcx.impl_trait_ref(def_id).unwrap(); - let imp_simp = fast_reject::simplify_type(self.tcx, - imp.self_ty(), - true); + let imp_simp = fast_reject::simplify_type(self.tcx, imp.self_ty(), true); if let Some(imp_simp) = imp_simp { if simp != imp_simp { return None @@ -482,10 +546,11 @@ } } - fn report_similar_impl_candidates(&self, - impl_candidates: Vec<ty::TraitRef<'tcx>>, - err: &mut DiagnosticBuilder<'_>) - { + fn report_similar_impl_candidates( + &self, + impl_candidates: Vec<ty::TraitRef<'tcx>>, + err: &mut DiagnosticBuilder<'_>, + ) { if impl_candidates.is_empty() { return; } @@ -497,7 +562,7 @@ 4 }; - let normalize = |candidate| self.tcx.global_tcx().infer_ctxt().enter(|ref infcx| { + let normalize = |candidate| self.tcx.infer_ctxt().enter(|ref infcx| { let normalized = infcx .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) .normalize(candidate) @@ -535,23 +600,33 @@ /// whose result could not be truly determined and thus we can't say /// if the program type checks or not -- and they are unusual /// occurrences in any case. - pub fn report_overflow_error<T>(&self, - obligation: &Obligation<'tcx, T>, - suggest_increasing_limit: bool) -> ! + pub fn report_overflow_error<T>( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! where T: fmt::Display + TypeFoldable<'tcx> { let predicate = self.resolve_vars_if_possible(&obligation.predicate); - let mut err = struct_span_err!(self.tcx.sess, obligation.cause.span, E0275, - "overflow evaluating the requirement `{}`", - predicate); + let mut err = struct_span_err!( + self.tcx.sess, + obligation.cause.span, + E0275, + "overflow evaluating the requirement `{}`", + predicate + ); if suggest_increasing_limit { self.suggest_new_overflow_limit(&mut err); } - self.note_obligation_cause_code(&mut err, &obligation.predicate, &obligation.cause.code, - &mut vec![]); + self.note_obligation_cause_code( + &mut err, + &obligation.predicate, + &obligation.cause.code, + &mut vec![], + ); err.emit(); self.tcx.sess.abort_if_errors(); @@ -718,12 +793,23 @@ // these notes will often be of the form // "the type `T` can't be frobnicated" // which is somewhat confusing. - err.help(&format!("consider adding a `where {}` bound", - trait_ref.to_predicate())); - } else if !have_alt_message { - // Can't show anything else useful, try to find similar impls. - let impl_candidates = self.find_similar_impl_candidates(trait_ref); - self.report_similar_impl_candidates(impl_candidates, &mut err); + self.suggest_restricting_param_bound( + &mut err, + &trait_ref, + obligation.cause.body_id, + ); + } else { + if !have_alt_message { + // Can't show anything else useful, try to find similar impls. + let impl_candidates = self.find_similar_impl_candidates(trait_ref); + self.report_similar_impl_candidates(impl_candidates, &mut err); + } + self.suggest_change_mut( + &obligation, + &mut err, + &trait_ref, + points_at_arg, + ); } // If this error is due to `!: Trait` not implemented but `(): Trait` is @@ -784,17 +870,12 @@ } ty::Predicate::ObjectSafe(trait_def_id) => { - let violations = self.tcx.global_tcx() - .object_safety_violations(trait_def_id); - if let Some(err) = self.tcx.report_object_safety_error( + let violations = self.tcx.object_safety_violations(trait_def_id); + self.tcx.report_object_safety_error( span, trait_def_id, violations, - ) { - err - } else { - return; - } + ) } ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { @@ -877,7 +958,7 @@ let found_trait_ty = found_trait_ref.self_ty(); - let found_did = match found_trait_ty.sty { + let found_did = match found_trait_ty.kind { ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) => Some(did), ty::Adt(def, _) => Some(def.did), _ => None, @@ -887,13 +968,21 @@ self.tcx.hir().span_if_local(did) ).map(|sp| self.tcx.sess.source_map().def_span(sp)); // the sp could be an fn def - let found = match found_trait_ref.skip_binder().substs.type_at(1).sty { + if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { + // We check closures twice, with obligations flowing in different directions, + // but we want to complain about them only once. + return; + } + + self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); + + let found = match found_trait_ref.skip_binder().substs.type_at(1).kind { ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], _ => vec![ArgKind::empty()], }; let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1); - let expected = match expected_ty.sty { + let expected = match expected_ty.kind { ty::Tuple(ref tys) => tys.iter() .map(|t| ArgKind::from_expected_ty(t.expect_ty(), Some(span))).collect(), _ => vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())], @@ -921,12 +1010,8 @@ } TraitNotObjectSafe(did) => { - let violations = self.tcx.global_tcx().object_safety_violations(did); - if let Some(err) = self.tcx.report_object_safety_error(span, did, violations) { - err - } else { - return; - } + let violations = self.tcx.object_safety_violations(did); + self.tcx.report_object_safety_error(span, did, violations) } // already reported in the query @@ -948,6 +1033,175 @@ err.emit(); } + fn suggest_restricting_param_bound( + &self, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'_>, + body_id: hir::HirId, + ) { + let self_ty = trait_ref.self_ty(); + let (param_ty, projection) = match &self_ty.kind { + ty::Param(_) => (true, None), + ty::Projection(projection) => (false, Some(projection)), + _ => return, + }; + + let mut suggest_restriction = |generics: &hir::Generics, msg| { + let span = generics.where_clause.span_for_predicates_or_empty_place(); + if !span.from_expansion() && span.desugaring_kind().is_none() { + err.span_suggestion( + generics.where_clause.span_for_predicates_or_empty_place().shrink_to_hi(), + &format!("consider further restricting {}", msg), + format!( + "{} {} ", + if !generics.where_clause.predicates.is_empty() { + "," + } else { + " where" + }, + trait_ref.to_predicate(), + ), + Applicability::MachineApplicable, + ); + } + }; + + // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we + // don't suggest `T: Sized + ?Sized`. + let mut hir_id = body_id; + while let Some(node) = self.tcx.hir().find(hir_id) { + match node { + hir::Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Method(..), .. + }) if param_ty && self_ty == self.tcx.types.self_param => { + // Restricting `Self` for a single method. + suggest_restriction(&generics, "`Self`"); + return; + } + + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(_, _, generics, _), .. + }) | + hir::Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Method(..), .. + }) | + hir::Node::ImplItem(hir::ImplItem { + generics, + kind: hir::ImplItemKind::Method(..), .. + }) | + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, _, generics, _, _), .. + }) | + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(_, _, _, generics, ..), .. + }) if projection.is_some() => { + // Missing associated type bound. + suggest_restriction(&generics, "the associated type"); + return; + } + + hir::Node::Item(hir::Item { kind: hir::ItemKind::Struct(_, generics), span, .. }) | + hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics), span, .. }) | + hir::Node::Item(hir::Item { kind: hir::ItemKind::Union(_, generics), span, .. }) | + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, _, generics, ..), span, .. + }) | + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(_, _, _, generics, ..), span, .. + }) | + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(_, _, generics, _), span, .. + }) | + hir::Node::Item(hir::Item { + kind: hir::ItemKind::TyAlias(_, generics), span, .. + }) | + hir::Node::Item(hir::Item { + kind: hir::ItemKind::TraitAlias(generics, _), span, .. + }) | + hir::Node::Item(hir::Item { + kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), span, .. + }) | + hir::Node::TraitItem(hir::TraitItem { generics, span, .. }) | + hir::Node::ImplItem(hir::ImplItem { generics, span, .. }) + if param_ty => { + // Missing generic type parameter bound. + let restrict_msg = "consider further restricting this bound"; + let param_name = self_ty.to_string(); + for param in generics.params.iter().filter(|p| { + ¶m_name == std::convert::AsRef::<str>::as_ref(&p.name.ident().as_str()) + }) { + if param_name.starts_with("impl ") { + // `impl Trait` in argument: + // `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}` + err.span_suggestion( + param.span, + restrict_msg, + // `impl CurrentTrait + MissingTrait` + format!("{} + {}", param.name.ident(), trait_ref), + Applicability::MachineApplicable, + ); + } else if generics.where_clause.predicates.is_empty() && + param.bounds.is_empty() + { + // If there are no bounds whatsoever, suggest adding a constraint + // to the type parameter: + // `fn foo<T>(t: T) {}` → `fn foo<T: Trait>(t: T) {}` + err.span_suggestion( + param.span, + "consider restricting this bound", + format!("{}", trait_ref.to_predicate()), + Applicability::MachineApplicable, + ); + } else if !generics.where_clause.predicates.is_empty() { + // There is a `where` clause, so suggest expanding it: + // `fn foo<T>(t: T) where T: Debug {}` → + // `fn foo<T>(t: T) where T: Debug, T: Trait {}` + err.span_suggestion( + generics.where_clause.span().unwrap().shrink_to_hi(), + &format!( + "consider further restricting type parameter `{}`", + param_name, + ), + format!(", {}", trait_ref.to_predicate()), + Applicability::MachineApplicable, + ); + } else { + // If there is no `where` clause lean towards constraining to the + // type parameter: + // `fn foo<X: Bar, T>(t: T, x: X) {}` → `fn foo<T: Trait>(t: T) {}` + // `fn foo<T: Bar>(t: T) {}` → `fn foo<T: Bar + Trait>(t: T) {}` + let sp = param.span.with_hi(span.hi()); + let span = self.tcx.sess.source_map() + .span_through_char(sp, ':'); + if sp != param.span && sp != span { + // Only suggest if we have high certainty that the span + // covers the colon in `foo<T: Trait>`. + err.span_suggestion(span, restrict_msg, format!( + "{} + ", + trait_ref.to_predicate(), + ), Applicability::MachineApplicable); + } else { + err.span_label(param.span, &format!( + "consider adding a `where {}` bound", + trait_ref.to_predicate(), + )); + } + } + return; + } + } + + hir::Node::Crate => return, + + _ => {} + } + + hir_id = self.tcx.hir().get_parent_item(hir_id); + } + } + /// When encountering an assignment of an unsized trait, like `let x = ""[..];`, provide a /// suggestion to borrow the initializer in order to use have a slice instead. fn suggest_borrow_on_unsized_slice( @@ -959,7 +1213,7 @@ let parent_node = self.tcx.hir().get_parent_node(hir_id); if let Some(Node::Local(ref local)) = self.tcx.hir().find(parent_node) { if let Some(ref expr) = local.init { - if let hir::ExprKind::Index(_, _) = expr.node { + if let hir::ExprKind::Index(_, _) = expr.kind { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(expr.span) { err.span_suggestion( expr.span, @@ -982,7 +1236,7 @@ points_at_arg: bool, ) { let self_ty = trait_ref.self_ty(); - match self_ty.sty { + match self_ty.kind { ty::FnDef(def_id, _) => { // We tried to apply the bound to an `fn`. Check whether calling it would evaluate // to a type that *would* satisfy the trait binding. If it would, suggest calling @@ -1004,7 +1258,7 @@ Ok(EvaluationResult::EvaluatedToAmbig) => { if let Some(hir::Node::Item(hir::Item { ident, - node: hir::ItemKind::Fn(.., body_id), + kind: hir::ItemKind::Fn(.., body_id), .. })) = self.tcx.hir().get_if_local(def_id) { let body = self.tcx.hir().body(*body_id); @@ -1013,7 +1267,7 @@ "{}({})", ident, body.params.iter() - .map(|arg| match &arg.pat.node { + .map(|arg| match &arg.pat.kind { hir::PatKind::Binding(_, _, ident, None) if ident.name != kw::SelfLower => ident.to_string(), _ => "_".to_string(), @@ -1069,14 +1323,16 @@ let mut trait_type = trait_ref.self_ty(); for refs_remaining in 0..refs_number { - if let ty::Ref(_, t_type, _) = trait_type.sty { + if let ty::Ref(_, t_type, _) = trait_type.kind { trait_type = t_type; let substs = self.tcx.mk_substs_trait(trait_type, &[]); let new_trait_ref = ty::TraitRef::new(trait_ref.def_id, substs); - let new_obligation = Obligation::new(ObligationCause::dummy(), - obligation.param_env, - new_trait_ref.to_predicate()); + let new_obligation = Obligation::new( + ObligationCause::dummy(), + obligation.param_env, + new_trait_ref.to_predicate(), + ); if self.predicate_may_hold(&new_obligation) { let sp = self.tcx.sess.source_map() @@ -1098,6 +1354,77 @@ } } + /// Check if the trait bound is implemented for a different mutability and note it in the + /// final error. + fn suggest_change_mut( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + trait_ref: &ty::Binder<ty::TraitRef<'tcx>>, + points_at_arg: bool, + ) { + let span = obligation.cause.span; + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let refs_number = snippet.chars() + .filter(|c| !c.is_whitespace()) + .take_while(|c| *c == '&') + .count(); + if let Some('\'') = snippet.chars() + .filter(|c| !c.is_whitespace()) + .skip(refs_number) + .next() + { // Do not suggest removal of borrow from type arguments. + return; + } + let trait_ref = self.resolve_vars_if_possible(trait_ref); + if trait_ref.has_infer_types() { + // Do not ICE while trying to find if a reborrow would succeed on a trait with + // unresolved bindings. + return; + } + + if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind { + let trait_type = match mutability { + hir::Mutability::MutMutable => self.tcx.mk_imm_ref(region, t_type), + hir::Mutability::MutImmutable => self.tcx.mk_mut_ref(region, t_type), + }; + + let substs = self.tcx.mk_substs_trait(&trait_type, &[]); + let new_trait_ref = ty::TraitRef::new(trait_ref.skip_binder().def_id, substs); + let new_obligation = Obligation::new( + ObligationCause::dummy(), + obligation.param_env, + new_trait_ref.to_predicate(), + ); + + if self.evaluate_obligation_no_overflow( + &new_obligation, + ).must_apply_modulo_regions() { + let sp = self.tcx.sess.source_map() + .span_take_while(span, |c| c.is_whitespace() || *c == '&'); + if points_at_arg && + mutability == hir::Mutability::MutImmutable && + refs_number > 0 + { + err.span_suggestion( + sp, + "consider changing this borrow's mutability", + "&mut ".to_string(), + Applicability::MachineApplicable, + ); + } else { + err.note(&format!( + "`{}` is implemented for `{:?}`, but not for `{:?}`", + trait_ref, + trait_type, + trait_ref.skip_binder().self_ty(), + )); + } + } + } + } + } + fn suggest_semicolon_removal( &self, obligation: &PredicateObligation<'tcx>, @@ -1109,11 +1436,11 @@ let parent_node = hir.get_parent_node(obligation.cause.body_id); let node = hir.find(parent_node); if let Some(hir::Node::Item(hir::Item { - node: hir::ItemKind::Fn(decl, _, _, body_id), + kind: hir::ItemKind::Fn(decl, _, _, body_id), .. })) = node { let body = hir.body(*body_id); - if let hir::ExprKind::Block(blk, _) = &body.value.node { + if let hir::ExprKind::Block(blk, _) = &body.value.kind { if decl.output.span().overlaps(span) && blk.expr.is_none() && "()" == &trait_ref.self_ty().to_string() { @@ -1137,14 +1464,14 @@ pub fn get_fn_like_arguments(&self, node: Node<'_>) -> (Span, Vec<ArgKind>) { match node { Node::Expr(&hir::Expr { - node: hir::ExprKind::Closure(_, ref _decl, id, span, _), + kind: hir::ExprKind::Closure(_, ref _decl, id, span, _), .. }) => { (self.tcx.sess.source_map().def_span(span), self.tcx.hir().body(id).params.iter() .map(|arg| { if let hir::Pat { - node: hir::PatKind::Tuple(ref args, _), + kind: hir::PatKind::Tuple(ref args, _), span, .. } = *arg.pat { @@ -1166,21 +1493,21 @@ } Node::Item(&hir::Item { span, - node: hir::ItemKind::Fn(ref decl, ..), + kind: hir::ItemKind::Fn(ref decl, ..), .. }) | Node::ImplItem(&hir::ImplItem { span, - node: hir::ImplItemKind::Method(hir::MethodSig { ref decl, .. }, _), + kind: hir::ImplItemKind::Method(hir::MethodSig { ref decl, .. }, _), .. }) | Node::TraitItem(&hir::TraitItem { span, - node: hir::TraitItemKind::Method(hir::MethodSig { ref decl, .. }, _), + kind: hir::TraitItemKind::Method(hir::MethodSig { ref decl, .. }, _), .. }) => { (self.tcx.sess.source_map().def_span(span), decl.inputs.iter() - .map(|arg| match arg.clone().node { + .map(|arg| match arg.clone().kind { hir::TyKind::Tup(ref tys) => ArgKind::Tuple( Some(arg.span), vec![("_".to_owned(), "_".to_owned()); tys.len()] @@ -1343,7 +1670,7 @@ ) -> DiagnosticBuilder<'tcx> { fn build_fn_sig_string<'tcx>(tcx: TyCtxt<'tcx>, trait_ref: &ty::TraitRef<'tcx>) -> String { let inputs = trait_ref.substs.type_at(1); - let sig = if let ty::Tuple(inputs) = inputs.sty { + let sig = if let ty::Tuple(inputs) = inputs.kind { tcx.mk_fn_sig( inputs.iter().map(|k| k.expect_ty()), tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })), @@ -1408,11 +1735,7 @@ span: Span, trait_def_id: DefId, violations: Vec<ObjectSafetyViolation>, - ) -> Option<DiagnosticBuilder<'tcx>> { - if self.sess.trait_methods_not_found.borrow().contains(&span) { - // Avoid emitting error caused by non-existing method (#58734) - return None; - } + ) -> DiagnosticBuilder<'tcx> { let trait_str = self.def_path_str(trait_def_id); let span = self.sess.source_map().def_span(span); let mut err = struct_span_err!( @@ -1430,13 +1753,22 @@ }; } } - Some(err) + + if self.sess.trait_methods_not_found.borrow().contains(&span) { + // Avoid emitting error caused by non-existing method (#58734) + err.cancel(); + } + + err } } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>, - body_id: Option<hir::BodyId>) { + fn maybe_report_ambiguity( + &self, + obligation: &PredicateObligation<'tcx>, + body_id: Option<hir::BodyId>, + ) { // Unable to successfully determine, probably means // insufficient type information, but could mean // ambiguous impls. The latter *ought* to be a @@ -1445,9 +1777,13 @@ let predicate = self.resolve_vars_if_possible(&obligation.predicate); let span = obligation.cause.span; - debug!("maybe_report_ambiguity(predicate={:?}, obligation={:?})", - predicate, - obligation); + debug!( + "maybe_report_ambiguity(predicate={:?}, obligation={:?} body_id={:?}, code={:?})", + predicate, + obligation, + body_id, + obligation.cause.code, + ); // Ambiguity errors are often caused as fallout from earlier // errors. So just ignore them if this infcx is tainted. @@ -1459,6 +1795,8 @@ ty::Predicate::Trait(ref data) => { let trait_ref = data.to_poly_trait_ref(); let self_ty = trait_ref.self_ty(); + debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind, trait_ref); + if predicate.references_error() { return; } @@ -1483,24 +1821,25 @@ // be ignoring the fact that we don't KNOW the type works // out. Though even that would probably be harmless, given that // we're only talking about builtin traits, which are known to be - // inhabited. But in any case I just threw in this check for - // has_errors() to be sure that compilation isn't happening - // anyway. In that case, why inundate the user. - if !self.tcx.sess.has_errors() { - if - self.tcx.lang_items().sized_trait() - .map_or(false, |sized_id| sized_id == trait_ref.def_id()) - { - self.need_type_info_err(body_id, span, self_ty).emit(); - } else { - let mut err = struct_span_err!(self.tcx.sess, - span, E0283, - "type annotations required: \ - cannot resolve `{}`", - predicate); - self.note_obligation_cause(&mut err, obligation); - err.emit(); - } + // inhabited. We used to check for `self.tcx.sess.has_errors()` to + // avoid inundating the user with unnecessary errors, but we now + // check upstream for type errors and dont add the obligations to + // begin with in those cases. + if + self.tcx.lang_items().sized_trait() + .map_or(false, |sized_id| sized_id == trait_ref.def_id()) + { + self.need_type_info_err(body_id, span, self_ty).emit(); + } else { + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0283, + "type annotations needed: cannot resolve `{}`", + predicate, + ); + self.note_obligation_cause(&mut err, obligation); + err.emit(); } } @@ -1527,11 +1866,13 @@ _ => { if !self.tcx.sess.has_errors() { - let mut err = struct_span_err!(self.tcx.sess, - obligation.cause.span, E0284, - "type annotations required: \ - cannot resolve `{}`", - predicate); + let mut err = struct_span_err!( + self.tcx.sess, + obligation.cause.span, + E0284, + "type annotations needed: cannot resolve `{}`", + predicate, + ); self.note_obligation_cause(&mut err, obligation); err.emit(); } @@ -1555,7 +1896,7 @@ fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.infcx.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Param(ty::ParamTy {name, .. }) = ty.sty { + if let ty::Param(ty::ParamTy {name, .. }) = ty.kind { let infcx = self.infcx; self.var_map.entry(ty).or_insert_with(|| infcx.next_ty_var( @@ -1675,8 +2016,8 @@ ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) | ObligationCauseCode::ImplDerivedObligation(derived_obligation) => { debug!("note_obligation_cause_for_async_await: self_ty.kind={:?}", - derived_obligation.parent_trait_ref.self_ty().sty); - match derived_obligation.parent_trait_ref.self_ty().sty { + derived_obligation.parent_trait_ref.self_ty().kind); + match derived_obligation.parent_trait_ref.self_ty().kind { ty::Adt(ty::AdtDef { did, .. }, ..) if self.tcx.is_diagnostic_item(sym::gen_future, *did) => {}, ty::Generator(did, ..) => generator = generator.or(Some(did)), @@ -1699,7 +2040,7 @@ .and_then(|parent_did| self.tcx.hir().get_if_local(parent_did)); debug!("note_obligation_cause_for_async_await: parent_node={:?}", parent_node); if let Some(hir::Node::Item(hir::Item { - node: hir::ItemKind::Fn(_, header, _, _), + kind: hir::ItemKind::Fn(_, header, _, _), .. })) = parent_node { debug!("note_obligation_cause_for_async_await: header={:?}", header); @@ -1829,9 +2170,22 @@ err.note(&format!("required for the cast to the object type `{}`", self.ty_to_string(object_ty))); } - ObligationCauseCode::RepeatVec => { + ObligationCauseCode::Coercion { source: _, target } => { + err.note(&format!("required by cast to type `{}`", + self.ty_to_string(target))); + } + ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expression) => { err.note("the `Copy` trait is required because the \ repeated element will be copied"); + if suggest_const_in_array_repeat_expression { + err.note("this array initializer can be evaluated at compile-time, for more \ + information, see issue \ + https://github.com/rust-lang/rust/issues/49147"); + if tcx.sess.opts.unstable_features.is_nightly_build() { + err.help("add `#![feature(const_in_array_repeat_expression)]` to the \ + crate attributes to enable"); + } + } } ObligationCauseCode::VariableType(_) => { err.note("all local variables must have a statically known size"); @@ -1884,6 +2238,9 @@ ObligationCauseCode::ConstSized => { err.note("constant expressions must have a statically known size"); } + ObligationCauseCode::ConstPatternStructural => { + err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`"); + } ObligationCauseCode::SharedStatic => { err.note("shared static variables must have a type that implements `Sync`"); } @@ -1919,7 +2276,8 @@ but not on the corresponding trait method", predicate)); } - ObligationCauseCode::ReturnType(_) | + ObligationCauseCode::ReturnType | + ObligationCauseCode::ReturnValue(_) | ObligationCauseCode::BlockTailExpression(_) => (), ObligationCauseCode::TrivialBound => { err.help("see issue #48214"); @@ -1929,6 +2287,12 @@ ); } } + ObligationCauseCode::AssocTypeBound(impl_span, orig) => { + err.span_label(orig, "associated type defined here"); + if let Some(sp) = impl_span { + err.span_label(sp, "in this `impl` item"); + } + } } } @@ -1974,7 +2338,7 @@ /// Creates an `ArgKind` from the expected type of an /// argument. It has no name (`_`) and an optional source span. pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind { - match t.sty { + match t.kind { ty::Tuple(ref tys) => ArgKind::Tuple( span, tys.iter()
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 805727b..a981162 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs
@@ -256,29 +256,46 @@ &mut self, pending_obligation: &mut Self::Obligation, ) -> ProcessResult<Self::Obligation, Self::Error> { - // If we were stalled on some unresolved variables, first check - // whether any of them have been resolved; if not, don't bother - // doing more work yet - if !pending_obligation.stalled_on.is_empty() { - let mut changed = false; - // This `for` loop was once a call to `all()`, but this lower-level - // form was a perf win. See #64545 for details. - for &ty in &pending_obligation.stalled_on { - if ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(ty) { - changed = true; - break; - } + // If we were stalled on some unresolved variables, first check whether + // any of them have been resolved; if not, don't bother doing more work + // yet. + let change = match pending_obligation.stalled_on.len() { + // Match arms are in order of frequency, which matters because this + // code is so hot. 1 and 0 dominate; 2+ is fairly rare. + 1 => { + let ty = pending_obligation.stalled_on[0]; + ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(ty) } - if !changed { - debug!("process_predicate: pending obligation {:?} still stalled on {:?}", - self.selcx.infcx() - .resolve_vars_if_possible(&pending_obligation.obligation), - pending_obligation.stalled_on); - return ProcessResult::Unchanged; + 0 => { + // In this case we haven't changed, but wish to make a change. + true } - pending_obligation.stalled_on = vec![]; + _ => { + // This `for` loop was once a call to `all()`, but this lower-level + // form was a perf win. See #64545 for details. + (|| { + for &ty in &pending_obligation.stalled_on { + if ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(ty) { + return true; + } + } + false + })() + } + }; + + if !change { + debug!("process_predicate: pending obligation {:?} still stalled on {:?}", + self.selcx.infcx() + .resolve_vars_if_possible(&pending_obligation.obligation), + pending_obligation.stalled_on); + return ProcessResult::Unchanged; } + // This part of the code is much colder. + + pending_obligation.stalled_on.truncate(0); + let obligation = &mut pending_obligation.obligation; if obligation.predicate.has_infer_types() { @@ -478,7 +495,7 @@ } else { if !substs.has_local_value() { let instance = ty::Instance::resolve( - self.selcx.tcx().global_tcx(), + self.selcx.tcx(), obligation.param_env, def_id, substs, @@ -531,7 +548,7 @@ .map(|t| selcx.infcx().resolve_vars_if_possible(&t)) .filter(|t| t.has_infer_types()) .flat_map(|t| t.walk()) - .filter(|t| match t.sty { ty::Infer(_) => true, _ => false }) + .filter(|t| match t.kind { ty::Infer(_) => true, _ => false }) .collect() }
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 1123422..b827529 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs
@@ -188,6 +188,9 @@ /// Obligation incurred due to an object cast. ObjectCastObligation(/* Object type */ Ty<'tcx>), + /// Obligation incurred due to a coercion. + Coercion { source: Ty<'tcx>, target: Ty<'tcx> }, + // Various cases where expressions must be sized/copy/etc: /// L = X implies that L is Sized AssignmentLhsSized, @@ -203,8 +206,9 @@ SizedReturnType, /// Yield type must be Sized SizedYieldType, - /// [T,..n] --> T must be Copy - RepeatVec, + /// [T,..n] --> T must be Copy. If `true`, suggest `const_in_array_repeat_expression` feature + /// flag. + RepeatVec(bool), /// Types of fields (other than the last, except for packed structs) in a struct must be sized. FieldSized { adt_kind: AdtKind, last: bool }, @@ -212,14 +216,14 @@ /// Constant expressions must be sized. ConstSized, - /// static items must have `Sync` type + /// Static items must have `Sync` type SharedStatic, BuiltinDerivedObligation(DerivedObligationCause<'tcx>), ImplDerivedObligation(DerivedObligationCause<'tcx>), - /// error derived when matching traits/impls; see ObligationCause for more details + /// Error derived when matching traits/impls; see ObligationCause for more details CompareImplMethodObligation { item_name: ast::Name, impl_item_def_id: DefId, @@ -236,6 +240,9 @@ /// Computing common supertype in the pattern guard for the arms of a match expression MatchExpressionArmPattern { span: Span, ty: Ty<'tcx> }, + /// Constants in patterns must have `Structural` type. + ConstPatternStructural, + /// Computing common supertype in an if expression IfExpression(Box<IfExpressionCause>), @@ -248,23 +255,28 @@ /// `start` has wrong type StartFunctionType, - /// intrinsic has wrong type + /// Intrinsic has wrong type IntrinsicType, - /// method receiver + /// Method receiver MethodReceiver, /// `return` with no expression ReturnNoExpression, /// `return` with an expression - ReturnType(hir::HirId), + ReturnValue(hir::HirId), + + /// Return type of this function + ReturnType, /// Block implicit return BlockTailExpression(hir::HirId), /// #[feature(trivial_bounds)] is not enabled TrivialBound, + + AssocTypeBound(/*impl*/ Option<Span>, /*original*/ Span), } // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. @@ -607,7 +619,7 @@ #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] pub struct VtableGeneratorData<'tcx, N> { pub generator_def_id: DefId, - pub substs: ty::GeneratorSubsts<'tcx>, + pub substs: SubstsRef<'tcx>, /// Nested obligations. This can be non-empty if the generator /// signature contains associated types. pub nested: Vec<N> @@ -616,7 +628,7 @@ #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] pub struct VtableClosureData<'tcx, N> { pub closure_def_id: DefId, - pub substs: ty::ClosureSubsts<'tcx>, + pub substs: SubstsRef<'tcx>, /// Nested obligations. This can be non-empty if the closure /// signature contains associated types. pub nested: Vec<N>
diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs index a7990c4..8ded141 100644 --- a/src/librustc/traits/object_safety.rs +++ b/src/librustc/traits/object_safety.rs
@@ -19,7 +19,7 @@ use std::borrow::Cow; use std::iter::{self}; use syntax::ast::{self}; -use syntax::symbol::InternedString; +use syntax::symbol::Symbol; use syntax_pos::{Span, DUMMY_SP}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -520,9 +520,11 @@ /// a pointer. /// /// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result - /// in a new check that `Trait` is object safe, creating a cycle. So instead, we fudge a little - /// by introducing a new type parameter `U` such that `Self: Unsize<U>` and `U: Trait + ?Sized`, - /// and use `U` in place of `dyn Trait`. Written as a chalk-style query: + /// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch + /// is stabilized, see tracking issue https://github.com/rust-lang/rust/issues/43561). + /// Instead, we fudge a little by introducing a new type parameter `U` such that + /// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`. + /// Written as a chalk-style query: /// /// forall (U: Trait + ?Sized) { /// if (Self: Unsize<U>) { @@ -556,11 +558,11 @@ // the type `U` in the query // use a bogus type parameter to mimick a forall(U) query using u32::MAX for now. - // FIXME(mikeyhew) this is a total hack, and we should replace it when real forall queries - // are implemented + // FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can + // replace this with `dyn Trait` let unsized_self_ty: Ty<'tcx> = self.mk_ty_param( ::std::u32::MAX, - InternedString::intern("RustaceansAreAwesome"), + Symbol::intern("RustaceansAreAwesome"), ); // `Receiver[Self => U]` @@ -677,7 +679,7 @@ let mut error = false; let self_ty = self.types.self_param; ty.maybe_walk(|ty| { - match ty.sty { + match ty.kind { ty::Param(_) => { if ty == self_ty { error = true;
diff --git a/src/librustc/traits/on_unimplemented.rs b/src/librustc/traits/on_unimplemented.rs index 5a988d9..b39c00a 100644 --- a/src/librustc/traits/on_unimplemented.rs +++ b/src/librustc/traits/on_unimplemented.rs
@@ -248,9 +248,11 @@ Position::ArgumentNamed(s) if s == sym::from_method => (), // `{from_desugaring}` is allowed Position::ArgumentNamed(s) if s == sym::from_desugaring => (), + // `{ItemContext}` is allowed + Position::ArgumentNamed(s) if s == sym::item_context => (), // So is `{A}` if A is a type parameter Position::ArgumentNamed(s) => match generics.params.iter().find(|param| { - param.name.as_symbol() == s + param.name == s }) { Some(_) => (), None => { @@ -289,13 +291,14 @@ }, GenericParamDefKind::Lifetime => return None }; - let name = param.name.as_symbol(); + let name = param.name; Some((name, value)) }).collect::<FxHashMap<Symbol, String>>(); let empty_string = String::new(); let s = self.0.as_str(); let parser = Parser::new(&s, None, vec![], false); + let item_context = (options.get(&sym::item_context)).unwrap_or(&empty_string); parser.map(|p| match p { Piece::String(s) => s, @@ -311,6 +314,8 @@ } else if s == sym::from_desugaring || s == sym::from_method { // don't break messages using these two arguments incorrectly &empty_string + } else if s == sym::item_context { + &item_context } else { bug!("broken on_unimplemented {:?} for {:?}: \ no argument matching {:?}",
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 87a23f6..d88bbe1 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs
@@ -337,7 +337,7 @@ // should occur eventually). let ty = ty.super_fold_with(self); - match ty.sty { + match ty.kind { ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { // (*) // Only normalize `impl Trait` after type-checking, usually in codegen. match self.param_env.reveal { @@ -921,7 +921,7 @@ let tcx = selcx.tcx(); // Check whether the self-type is itself a projection. - let (def_id, substs) = match obligation_trait_ref.self_ty().sty { + let (def_id, substs) = match obligation_trait_ref.self_ty().kind { ty::Projection(ref data) => { (data.trait_ref(tcx).def_id, data.substs) } @@ -1199,7 +1199,7 @@ let object_ty = selcx.infcx().shallow_resolve(self_ty); debug!("confirm_object_candidate(object_ty={:?})", object_ty); - let data = match object_ty.sty { + let data = match object_ty.kind { ty::Dynamic(ref data, ..) => data, _ => { span_bug!( @@ -1259,7 +1259,7 @@ obligation: &ProjectionTyObligation<'tcx>, vtable: VtableGeneratorData<'tcx, PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let gen_sig = vtable.substs.poly_sig(vtable.generator_def_id, selcx.tcx()); + let gen_sig = vtable.substs.as_generator().poly_sig(vtable.generator_def_id, selcx.tcx()); let Normalized { value: gen_sig, obligations @@ -1334,7 +1334,8 @@ ) -> Progress<'tcx> { let tcx = selcx.tcx(); let infcx = selcx.infcx(); - let closure_sig_ty = vtable.substs.closure_sig_ty(vtable.closure_def_id, tcx); + let closure_sig_ty = vtable.substs + .as_closure().sig_ty(vtable.closure_def_id, tcx); let closure_sig = infcx.shallow_resolve(closure_sig_ty).fn_sig(tcx); let Normalized { value: closure_sig, @@ -1504,8 +1505,8 @@ if let Some(assoc_item) = trait_def .ancestors(tcx, impl_def_id) - .defs(tcx, assoc_ty_name, ty::AssocKind::Type, trait_def_id) - .next() { + .leaf_def(tcx, assoc_ty_name, ty::AssocKind::Type) { + assoc_item } else { // This is saying that neither the trait nor
diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs index 46403a3..e84c91d 100644 --- a/src/librustc/traits/query/dropck_outlives.rs +++ b/src/librustc/traits/query/dropck_outlives.rs
@@ -3,8 +3,9 @@ use crate::infer::canonical::OriginalQueryValues; use std::iter::FromIterator; use syntax::source_map::Span; -use crate::ty::subst::Kind; +use crate::ty::subst::GenericArg; use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::query::Providers; impl<'cx, 'tcx> At<'cx, 'tcx> { /// Given a type `ty` of some value being dropped, computes a set @@ -24,7 +25,7 @@ /// /// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md /// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md - pub fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec<Kind<'tcx>>> { + pub fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec<GenericArg<'tcx>>> { debug!( "dropck_outlives(ty={:?}, param_env={:?})", ty, self.param_env, @@ -33,19 +34,18 @@ // Quick check: there are a number of cases that we know do not require // any destructor. let tcx = self.infcx.tcx; - if trivial_dropck_outlives(tcx, ty) { + if tcx.trivial_dropck_outlives(ty) { return InferOk { value: vec![], obligations: vec![], }; } - let gcx = tcx.global_tcx(); let mut orig_values = OriginalQueryValues::default(); let c_ty = self.infcx.canonicalize_query(&self.param_env.and(ty), &mut orig_values); let span = self.cause.span; debug!("c_ty = {:?}", c_ty); - if let Ok(result) = &gcx.dropck_outlives(c_ty) { + if let Ok(result) = &tcx.dropck_outlives(c_ty) { if result.is_proven() { if let Ok(InferOk { value, obligations }) = self.infcx.instantiate_query_response_and_region_obligations( @@ -80,7 +80,7 @@ #[derive(Clone, Debug, Default)] pub struct DropckOutlivesResult<'tcx> { - pub kinds: Vec<Kind<'tcx>>, + pub kinds: Vec<GenericArg<'tcx>>, pub overflows: Vec<Ty<'tcx>>, } @@ -104,7 +104,7 @@ tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>, - ) -> Vec<Kind<'tcx>> { + ) -> Vec<GenericArg<'tcx>> { self.report_overflows(tcx, span, ty); let DropckOutlivesResult { kinds, overflows: _ } = self; kinds @@ -117,7 +117,7 @@ pub struct DtorckConstraint<'tcx> { /// Types that are required to be alive in order for this /// type to be valid for destruction. - pub outlives: Vec<ty::subst::Kind<'tcx>>, + pub outlives: Vec<ty::subst::GenericArg<'tcx>>, /// Types that could not be resolved: projections and params. pub dtorck_types: Vec<Ty<'tcx>>, @@ -186,7 +186,7 @@ /// Note also that `needs_drop` requires a "global" type (i.e., one /// with erased regions), but this function does not. pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.sty { + match ty.kind { // None of these types have a destructor and hence they do not // require anything in particular to outlive the dtor's // execution. @@ -208,14 +208,15 @@ | ty::Error => true, // [T; N] and [T] have same properties as T. - ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), + ty::Array(ty, _) | ty::Slice(ty) => tcx.trivial_dropck_outlives(ty), // (T1..Tn) and closures have same properties as T1..Tn -- // check if *any* of those are trivial. - ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())), + ty::Tuple(ref tys) => tys.iter().all(|t| tcx.trivial_dropck_outlives(t.expect_ty())), ty::Closure(def_id, ref substs) => substs + .as_closure() .upvar_tys(def_id, tcx) - .all(|t| trivial_dropck_outlives(tcx, t)), + .all(|t| tcx.trivial_dropck_outlives(t)), ty::Adt(def, _) => { if Some(def.did) == tcx.lang_items().manually_drop() { @@ -243,3 +244,10 @@ ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), } } + +crate fn provide(p: &mut Providers<'_>) { + *p = Providers { + trivial_dropck_outlives, + ..*p + }; +}
diff --git a/src/librustc/traits/query/evaluate_obligation.rs b/src/librustc/traits/query/evaluate_obligation.rs index b9557ce..0d426ca 100644 --- a/src/librustc/traits/query/evaluate_obligation.rs +++ b/src/librustc/traits/query/evaluate_obligation.rs
@@ -50,13 +50,13 @@ // Run canonical query. If overflow occurs, rerun from scratch but this time // in standard trait query mode so that overflow is handled appropriately // within `SelectionContext`. - self.tcx.global_tcx().evaluate_obligation(c_pred) + self.tcx.evaluate_obligation(c_pred) } // Helper function that canonicalizes and runs the query. If an // overflow results, we re-run it in the local context so we can // report a nice error. - fn evaluate_obligation_no_overflow( + crate fn evaluate_obligation_no_overflow( &self, obligation: &PredicateObligation<'tcx>, ) -> EvaluationResult {
diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index 112a1d0..f6ea77d 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs
@@ -40,7 +40,7 @@ pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::normalize::Normalize<T>>>; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug)] pub struct NoSolution; pub type Fallible<T> = Result<T, NoSolution>;
diff --git a/src/librustc/traits/query/normalize.rs b/src/librustc/traits/query/normalize.rs index c31ff3a..ab42eab 100644 --- a/src/librustc/traits/query/normalize.rs +++ b/src/librustc/traits/query/normalize.rs
@@ -88,7 +88,7 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { let ty = ty.super_fold_with(self); - match ty.sty { + match ty.kind { ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { // (*) // Only normalize `impl Trait` after type-checking, usually in codegen. @@ -141,7 +141,7 @@ // binder). It would be better to normalize in a // binding-aware fashion. - let gcx = self.infcx.tcx.global_tcx(); + let tcx = self.infcx.tcx; let mut orig_values = OriginalQueryValues::default(); // HACK(matthewjasper) `'static` is special-cased in selection, @@ -150,7 +150,7 @@ &self.param_env.and(*data), &mut orig_values); debug!("QueryNormalizer: c_data = {:#?}", c_data); debug!("QueryNormalizer: orig_values = {:#?}", orig_values); - match gcx.normalize_projection_ty(c_data) { + match tcx.normalize_projection_ty(c_data) { Ok(result) => { // We don't expect ambiguity. if result.is_ambiguous() {
diff --git a/src/librustc/traits/query/outlives_bounds.rs b/src/librustc/traits/query/outlives_bounds.rs index 40bd187..eee084b 100644 --- a/src/librustc/traits/query/outlives_bounds.rs +++ b/src/librustc/traits/query/outlives_bounds.rs
@@ -7,8 +7,7 @@ use crate::ty::{self, Ty, TyCtxt}; use crate::ich::StableHashingContext; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, - StableHasherResult}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use std::mem; /// Outlives bounds are relationships between generic parameters, @@ -43,9 +42,7 @@ } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for OutlivesBound<'tcx> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { OutlivesBound::RegionSubRegion(ref a, ref b) => { @@ -97,7 +94,7 @@ let mut orig_values = OriginalQueryValues::default(); let key = self.canonicalize_query(¶m_env.and(ty), &mut orig_values); - let result = match self.tcx.global_tcx().implied_outlives_bounds(key) { + let result = match self.tcx.implied_outlives_bounds(key) { Ok(r) => r, Err(NoSolution) => { self.tcx.sess.delay_span_bug(
diff --git a/src/librustc/traits/query/type_op/ascribe_user_type.rs b/src/librustc/traits/query/type_op/ascribe_user_type.rs index 05a4d43..34aa4ee 100644 --- a/src/librustc/traits/query/type_op/ascribe_user_type.rs +++ b/src/librustc/traits/query/type_op/ascribe_user_type.rs
@@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, QueryResponse}; +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::Fallible; use crate::hir::def_id::DefId; use crate::ty::{ParamEnvAnd, Ty, TyCtxt}; @@ -37,12 +37,6 @@ ) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> { tcx.type_op_ascribe_user_type(canonicalized) } - - fn shrink_to_tcx_lifetime( - v: &'a CanonicalizedQueryResponse<'tcx, ()>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, ()>> { - v - } } BraceStructTypeFoldableImpl! {
diff --git a/src/librustc/traits/query/type_op/eq.rs b/src/librustc/traits/query/type_op/eq.rs index e8ec304..3653f92 100644 --- a/src/librustc/traits/query/type_op/eq.rs +++ b/src/librustc/traits/query/type_op/eq.rs
@@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, QueryResponse}; +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::Fallible; use crate::ty::{ParamEnvAnd, Ty, TyCtxt}; @@ -34,12 +34,6 @@ ) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> { tcx.type_op_eq(canonicalized) } - - fn shrink_to_tcx_lifetime( - v: &'a CanonicalizedQueryResponse<'tcx, ()>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, ()>> { - v - } } BraceStructTypeFoldableImpl! {
diff --git a/src/librustc/traits/query/type_op/implied_outlives_bounds.rs b/src/librustc/traits/query/type_op/implied_outlives_bounds.rs index 3beb4d6..7aa9870 100644 --- a/src/librustc/traits/query/type_op/implied_outlives_bounds.rs +++ b/src/librustc/traits/query/type_op/implied_outlives_bounds.rs
@@ -1,9 +1,9 @@ -use crate::infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, QueryResponse}; +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::outlives_bounds::OutlivesBound; use crate::traits::query::Fallible; use crate::ty::{ParamEnvAnd, Ty, TyCtxt}; -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Clone, Debug)] pub struct ImpliedOutlivesBounds<'tcx> { pub ty: Ty<'tcx>, } @@ -38,12 +38,6 @@ tcx.implied_outlives_bounds(canonicalized) } - - fn shrink_to_tcx_lifetime( - v: &'a CanonicalizedQueryResponse<'tcx, Self::QueryResponse>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, Self::QueryResponse>> { - v - } } BraceStructTypeFoldableImpl! {
diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index e2a5cd9..98e5352 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs
@@ -1,6 +1,6 @@ use crate::infer::canonical::{ - Canonical, Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, - QueryRegionConstraints, QueryResponse, + Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, + QueryRegionConstraints, }; use crate::infer::{InferCtxt, InferOk}; use std::fmt; @@ -66,22 +66,6 @@ canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>>; - /// Casts a lifted query result (which is in the gcx lifetime) - /// into the tcx lifetime. This is always just an identity cast, - /// but the generic code doesn't realize it -- put another way, in - /// the generic code, we have a `Lifted<'tcx, Self::QueryResponse>` - /// and we want to convert that to a `Self::QueryResponse`. This is - /// not a priori valid, so we can't do it -- but in practice, it - /// is always a no-op (e.g., the lifted form of a type, - /// `Ty<'tcx>`, is a subtype of `Ty<'tcx>`). So we have to push - /// the operation into the impls that know more specifically what - /// `QueryResponse` is. This operation would (maybe) be nicer with - /// something like HKTs or GATs, since then we could make - /// `QueryResponse` parametric and `'tcx` and `'tcx` etc. - fn shrink_to_tcx_lifetime( - lifted_query_result: &'a CanonicalizedQueryResponse<'tcx, Self::QueryResponse>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, Self::QueryResponse>>; - fn fully_perform_into( query_key: ParamEnvAnd<'tcx, Self>, infcx: &InferCtxt<'_, 'tcx>, @@ -99,7 +83,6 @@ let canonical_self = infcx.canonicalize_hr_query_hack(&query_key, &mut canonical_var_values); let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; - let canonical_result = Self::shrink_to_tcx_lifetime(&canonical_result); let param_env = query_key.param_env;
diff --git a/src/librustc/traits/query/type_op/normalize.rs b/src/librustc/traits/query/type_op/normalize.rs index 3fe85d8..2138f79 100644 --- a/src/librustc/traits/query/type_op/normalize.rs +++ b/src/librustc/traits/query/type_op/normalize.rs
@@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, QueryResponse}; +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use std::fmt; use crate::traits::query::Fallible; use crate::ty::fold::TypeFoldable; @@ -38,12 +38,6 @@ ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>> { T::type_op_method(tcx, canonicalized) } - - fn shrink_to_tcx_lifetime( - v: &'a CanonicalizedQueryResponse<'tcx, T>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, T>> { - T::shrink_to_tcx_lifetime(v) - } } pub trait Normalizable<'tcx>: fmt::Debug + TypeFoldable<'tcx> + Lift<'tcx> + Copy { @@ -51,12 +45,6 @@ tcx: TyCtxt<'tcx>, canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>>; - - /// Converts from the `'tcx` (lifted) form of `Self` into the `tcx` - /// form of `Self`. - fn shrink_to_tcx_lifetime( - v: &'a CanonicalizedQueryResponse<'tcx, Self>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, Self>>; } impl Normalizable<'tcx> for Ty<'tcx> { @@ -66,12 +54,6 @@ ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> { tcx.type_op_normalize_ty(canonicalized) } - - fn shrink_to_tcx_lifetime( - v: &'a CanonicalizedQueryResponse<'tcx, Self>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, Self>> { - v - } } impl Normalizable<'tcx> for ty::Predicate<'tcx> { @@ -81,12 +63,6 @@ ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> { tcx.type_op_normalize_predicate(canonicalized) } - - fn shrink_to_tcx_lifetime( - v: &'a CanonicalizedQueryResponse<'tcx, Self>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, Self>> { - v - } } impl Normalizable<'tcx> for ty::PolyFnSig<'tcx> { @@ -96,12 +72,6 @@ ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> { tcx.type_op_normalize_poly_fn_sig(canonicalized) } - - fn shrink_to_tcx_lifetime( - v: &'a CanonicalizedQueryResponse<'tcx, Self>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, Self>> { - v - } } impl Normalizable<'tcx> for ty::FnSig<'tcx> { @@ -111,12 +81,6 @@ ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> { tcx.type_op_normalize_fn_sig(canonicalized) } - - fn shrink_to_tcx_lifetime( - v: &'a CanonicalizedQueryResponse<'tcx, Self>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, Self>> { - v - } } BraceStructTypeFoldableImpl! {
diff --git a/src/librustc/traits/query/type_op/outlives.rs b/src/librustc/traits/query/type_op/outlives.rs index d4b3635..86a32d6 100644 --- a/src/librustc/traits/query/type_op/outlives.rs +++ b/src/librustc/traits/query/type_op/outlives.rs
@@ -1,5 +1,4 @@ -use crate::infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, QueryResponse}; -use crate::traits::query::dropck_outlives::trivial_dropck_outlives; +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::dropck_outlives::DropckOutlivesResult; use crate::traits::query::Fallible; use crate::ty::{ParamEnvAnd, Ty, TyCtxt}; @@ -22,7 +21,7 @@ tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>, ) -> Option<Self::QueryResponse> { - if trivial_dropck_outlives(tcx, key.value.dropped_ty) { + if tcx.trivial_dropck_outlives(key.value.dropped_ty) { Some(DropckOutlivesResult::default()) } else { None @@ -53,12 +52,6 @@ tcx.dropck_outlives(canonicalized) } - - fn shrink_to_tcx_lifetime( - lifted_query_result: &'a CanonicalizedQueryResponse<'tcx, Self::QueryResponse>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, Self::QueryResponse>> { - lifted_query_result - } } BraceStructTypeFoldableImpl! {
diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs index 1efe663..2a908d0 100644 --- a/src/librustc/traits/query/type_op/prove_predicate.rs +++ b/src/librustc/traits/query/type_op/prove_predicate.rs
@@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, QueryResponse}; +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::Fallible; use crate::ty::{ParamEnvAnd, Predicate, TyCtxt}; @@ -43,12 +43,6 @@ ) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> { tcx.type_op_prove_predicate(canonicalized) } - - fn shrink_to_tcx_lifetime( - v: &'a CanonicalizedQueryResponse<'tcx, ()>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, ()>> { - v - } } BraceStructTypeFoldableImpl! {
diff --git a/src/librustc/traits/query/type_op/subtype.rs b/src/librustc/traits/query/type_op/subtype.rs index 71c7499..c89a55d 100644 --- a/src/librustc/traits/query/type_op/subtype.rs +++ b/src/librustc/traits/query/type_op/subtype.rs
@@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, QueryResponse}; +use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; use crate::traits::query::Fallible; use crate::ty::{ParamEnvAnd, Ty, TyCtxt}; @@ -34,12 +34,6 @@ ) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> { tcx.type_op_subtype(canonicalized) } - - fn shrink_to_tcx_lifetime( - v: &'a CanonicalizedQueryResponse<'tcx, ()>, - ) -> &'a Canonical<'tcx, QueryResponse<'tcx, ()>> { - v - } } BraceStructTypeFoldableImpl! {
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index a54bc05..d8a27f1 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs
@@ -40,9 +40,11 @@ use crate::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable}; use crate::hir; -use rustc_data_structures::bit_set::GrowableBitSet; +use rustc_index::bit_set::GrowableBitSet; use rustc_data_structures::sync::Lock; use rustc_target::spec::abi::Abi; +use syntax::attr; +use syntax::symbol::sym; use std::cell::{Cell, RefCell}; use std::cmp; use std::fmt::{self, Display}; @@ -99,6 +101,9 @@ trait_desc: String, self_desc: Option<String>, }, + ReservationImpl { + message: String + }, } impl IntercrateAmbiguityCause { @@ -139,6 +144,11 @@ trait_desc, self_desc ) } + &IntercrateAmbiguityCause::ReservationImpl { + ref message + } => { + message.clone() + } } } } @@ -214,7 +224,7 @@ /// of type variables - it just means the obligation isn't sufficiently /// elaborated. In that case we report an ambiguity, and the caller can /// try again after more type information has been gathered or report a -/// "type annotations required" error. +/// "type annotations needed" error. /// /// However, with type parameters, this can be a real problem - type /// parameters don't unify with regular types, but they *can* unify @@ -1326,17 +1336,38 @@ (result, dep_node) } - // Treat negative impls as unimplemented - fn filter_negative_impls( - &self, + // Treat negative impls as unimplemented, and reservation impls as ambiguity. + fn filter_negative_and_reservation_impls( + &mut self, candidate: SelectionCandidate<'tcx>, ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { if let ImplCandidate(def_id) = candidate { - if !self.allow_negative_impls - && self.tcx().impl_polarity(def_id) == hir::ImplPolarity::Negative - { - return Err(Unimplemented); - } + let tcx = self.tcx(); + match tcx.impl_polarity(def_id) { + ty::ImplPolarity::Negative if !self.allow_negative_impls => { + return Err(Unimplemented); + } + ty::ImplPolarity::Reservation => { + if let Some(intercrate_ambiguity_clauses) + = &mut self.intercrate_ambiguity_causes + { + let attrs = tcx.get_attrs(def_id); + let attr = attr::find_by_name(&attrs, sym::rustc_reservation_impl); + let value = attr.and_then(|a| a.value_str()); + if let Some(value) = value { + debug!("filter_negative_and_reservation_impls: \ + reservation impl ambiguity on {:?}", def_id); + intercrate_ambiguity_clauses.push( + IntercrateAmbiguityCause::ReservationImpl { + message: value.to_string() + } + ); + } + } + return Ok(None); + } + _ => {} + }; } Ok(Some(candidate)) } @@ -1453,7 +1484,7 @@ // Instead, we select the right impl now but report `Bar does // not implement Clone`. if candidates.len() == 1 { - return self.filter_negative_impls(candidates.pop().unwrap()); + return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); } // Winnow, but record the exact outcome of evaluation, which @@ -1528,7 +1559,7 @@ } // Just one candidate left. - self.filter_negative_impls(candidates.pop().unwrap().candidate) + self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) } fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<Conflict> { @@ -1785,7 +1816,7 @@ // before we go into the whole placeholder thing, just // quickly check if the self-type is a projection at all. - match obligation.predicate.skip_binder().trait_ref.self_ty().sty { + match obligation.predicate.skip_binder().trait_ref.self_ty().kind { ty::Projection(_) | ty::Opaque(..) => {} ty::Infer(ty::TyVar(_)) => { span_bug!( @@ -1823,7 +1854,7 @@ placeholder_trait_predicate, ); - let (def_id, substs) = match placeholder_trait_predicate.trait_ref.self_ty().sty { + let (def_id, substs) = match placeholder_trait_predicate.trait_ref.self_ty().kind { ty::Projection(ref data) => (data.trait_ref(self.tcx()).def_id, data.substs), ty::Opaque(def_id, substs) => (def_id, substs), _ => { @@ -1971,7 +2002,7 @@ // touch bound regions, they just capture the in-scope // type/region parameters. let self_ty = *obligation.self_ty().skip_binder(); - match self_ty.sty { + match self_ty.kind { ty::Generator(..) => { debug!( "assemble_generator_candidates: self_ty={:?} obligation={:?}", @@ -2014,13 +2045,16 @@ // Okay to skip binder because the substs on closure types never // touch bound regions, they just capture the in-scope // type/region parameters - match obligation.self_ty().skip_binder().sty { + match obligation.self_ty().skip_binder().kind { ty::Closure(closure_def_id, closure_substs) => { debug!( "assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation ); - match self.infcx.closure_kind(closure_def_id, closure_substs) { + match self.infcx.closure_kind( + closure_def_id, + closure_substs + ) { Some(closure_kind) => { debug!( "assemble_unboxed_candidates: closure_kind = {:?}", @@ -2063,7 +2097,7 @@ // Okay to skip binder because what we are inspecting doesn't involve bound regions let self_ty = *obligation.self_ty().skip_binder(); - match self_ty.sty { + match self_ty.kind { ty::Infer(ty::TyVar(_)) => { debug!("assemble_fn_pointer_candidates: ambiguous self-type"); candidates.ambiguous = true; // could wind up being a fn() type @@ -2125,7 +2159,7 @@ let def_id = obligation.predicate.def_id(); if self.tcx().trait_is_auto(def_id) { - match self_ty.sty { + match self_ty.kind { ty::Dynamic(..) => { // For object types, we don't know what the closed // over types are. This means we conservatively @@ -2198,7 +2232,7 @@ // self-ty here doesn't escape this probe, so just erase // any LBR. let self_ty = self.tcx().erase_late_bound_regions(&obligation.self_ty()); - let poly_trait_ref = match self_ty.sty { + let poly_trait_ref = match self_ty.kind { ty::Dynamic(ref data, ..) => { if data.auto_traits() .any(|did| did == obligation.predicate.def_id()) @@ -2212,7 +2246,13 @@ } if let Some(principal) = data.principal() { - principal.with_self_ty(self.tcx(), self_ty) + if !self.infcx.tcx.features().object_safe_for_dispatch { + principal.with_self_ty(self.tcx(), self_ty) + } else if self.tcx().is_object_safe(principal.def_id()) { + principal.with_self_ty(self.tcx(), self_ty) + } else { + return; + } } else { // Only auto-trait bounds exist. return; @@ -2294,7 +2334,7 @@ source, target ); - let may_apply = match (&source.sty, &target.sty) { + let may_apply = match (&source.kind, &target.kind) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { // Upcasts permit two things: @@ -2460,7 +2500,7 @@ if other.evaluation.must_apply_modulo_regions() { match victim.candidate { ImplCandidate(victim_def) => { - let tcx = self.tcx().global_tcx(); + let tcx = self.tcx(); return tcx.specializes((other_def, victim_def)) || tcx.impls_are_allowed_to_overlap( other_def, victim_def).is_some(); @@ -2532,7 +2572,7 @@ let self_ty = self.infcx .shallow_resolve(obligation.predicate.skip_binder().self_ty()); - match self_ty.sty { + match self_ty.kind { ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::Uint(_) @@ -2598,7 +2638,7 @@ use self::BuiltinImplConditions::{Ambiguous, None, Where}; - match self_ty.sty { + match self_ty.kind { ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::FnDef(..) @@ -2638,7 +2678,7 @@ ty::Closure(def_id, substs) => { // (*) binder moved here Where(ty::Binder::bind( - substs.upvar_tys(def_id, self.tcx()).collect(), + substs.as_closure().upvar_tys(def_id, self.tcx()).collect(), )) } @@ -2680,7 +2720,7 @@ /// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32] /// ``` fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec<Ty<'tcx>> { - match t.sty { + match t.kind { ty::Uint(_) | ty::Int(_) | ty::Bool @@ -2722,11 +2762,14 @@ tys.iter().map(|k| k.expect_ty()).collect() } - ty::Closure(def_id, ref substs) => substs.upvar_tys(def_id, self.tcx()).collect(), + ty::Closure(def_id, ref substs) => substs.as_closure() + .upvar_tys(def_id, self.tcx()) + .collect(), ty::Generator(def_id, ref substs, _) => { - let witness = substs.witness(def_id, self.tcx()); + let witness = substs.as_generator().witness(def_id, self.tcx()); substs + .as_generator() .upvar_tys(def_id, self.tcx()) .chain(iter::once(witness)) .collect() @@ -2782,7 +2825,7 @@ // binder moved -\ let ty: ty::Binder<Ty<'tcx>> = ty::Binder::bind(ty); // <----/ - self.infcx.in_snapshot(|_| { + self.infcx.commit_unconditionally(|_| { let (skol_ty, _) = self.infcx .replace_bound_vars_with_placeholders(&ty); let Normalized { @@ -2895,7 +2938,7 @@ } fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) { - self.infcx.in_snapshot(|snapshot| { + self.infcx.commit_unconditionally(|snapshot| { let result = self.match_projection_obligation_against_definition_bounds( obligation, @@ -3017,19 +3060,20 @@ nested, ); - let trait_obligations: Vec<PredicateObligation<'_>> = self.infcx.in_snapshot(|_| { - let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); - let (trait_ref, _) = self.infcx - .replace_bound_vars_with_placeholders(&poly_trait_ref); - let cause = obligation.derived_cause(ImplDerivedObligation); - self.impl_or_trait_obligations( - cause, - obligation.recursion_depth + 1, - obligation.param_env, - trait_def_id, - &trait_ref.substs, - ) - }); + let trait_obligations: Vec<PredicateObligation<'_>> = + self.infcx.commit_unconditionally(|_| { + let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); + let (trait_ref, _) = self.infcx + .replace_bound_vars_with_placeholders(&poly_trait_ref); + let cause = obligation.derived_cause(ImplDerivedObligation); + self.impl_or_trait_obligations( + cause, + obligation.recursion_depth + 1, + obligation.param_env, + trait_def_id, + &trait_ref.substs, + ) + }); // Adds the predicates from the trait. Note that this contains a `Self: Trait` // predicate as usual. It won't have any effect since auto traits are coinductive. @@ -3052,7 +3096,7 @@ // First, create the substitutions by matching the impl again, // this time not in a probe. - self.infcx.in_snapshot(|snapshot| { + self.infcx.commit_unconditionally(|snapshot| { let substs = self.rematch_impl(impl_def_id, obligation, snapshot); debug!("confirm_impl_candidate: substs={:?}", substs); let cause = obligation.derived_cause(ImplDerivedObligation); @@ -3118,7 +3162,7 @@ // results. let self_ty = self.infcx .shallow_resolve(*obligation.self_ty().skip_binder()); - let poly_trait_ref = match self_ty.sty { + let poly_trait_ref = match self_ty.kind { ty::Dynamic(ref data, ..) => data.principal().unwrap_or_else(|| { span_bug!(obligation.cause.span, "object candidate with no principal") @@ -3216,7 +3260,7 @@ obligation, alias_def_id ); - self.infcx.in_snapshot(|_| { + self.infcx.commit_unconditionally(|_| { let (predicate, _) = self.infcx() .replace_bound_vars_with_placeholders(&obligation.predicate); let trait_ref = predicate.trait_ref; @@ -3252,7 +3296,7 @@ // touch bound regions, they just capture the in-scope // type/region parameters. let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); - let (generator_def_id, substs) = match self_ty.sty { + let (generator_def_id, substs) = match self_ty.kind { ty::Generator(id, substs, _) => (id, substs), _ => bug!("closure candidate for non-closure {:?}", obligation), }; @@ -3288,8 +3332,8 @@ )?); Ok(VtableGeneratorData { - generator_def_id: generator_def_id, - substs: substs.clone(), + generator_def_id, + substs, nested: obligations, }) } @@ -3309,7 +3353,7 @@ // touch bound regions, they just capture the in-scope // type/region parameters. let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); - let (closure_def_id, substs) = match self_ty.sty { + let (closure_def_id, substs) = match self_ty.kind { ty::Closure(id, substs) => (id, substs), _ => bug!("closure candidate for non-closure {:?}", obligation), }; @@ -3339,17 +3383,22 @@ )?); // FIXME: chalk + if !self.tcx().sess.opts.debugging_opts.chalk { obligations.push(Obligation::new( obligation.cause.clone(), obligation.param_env, - ty::Predicate::ClosureKind(closure_def_id, substs, kind), + ty::Predicate::ClosureKind( + closure_def_id, + substs, + kind + ), )); } Ok(VtableClosureData { closure_def_id, - substs: substs.clone(), + substs: substs, nested: obligations, }) } @@ -3418,7 +3467,7 @@ ); let mut nested = vec![]; - match (&source.sty, &target.sty) { + match (&source.kind, &target.kind) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { // See assemble_candidates_for_unsizing for more info. @@ -3550,7 +3599,7 @@ let mut ty_params = GrowableBitSet::new_empty(); let mut found = false; for ty in field.walk() { - if let ty::Param(p) = ty.sty { + if let ty::Param(p) = ty.kind { ty_params.insert(p.index as usize); found = true; } @@ -3728,6 +3777,13 @@ return Err(()); } + if self.intercrate.is_none() + && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation + { + debug!("match_impl: reservation impls only apply in intercrate mode"); + return Err(()); + } + debug!("match_impl: success impl_substs={:?}", impl_substs); Ok(Normalized { value: impl_substs, @@ -3831,7 +3887,7 @@ &mut self, obligation: &TraitObligation<'tcx>, closure_def_id: DefId, - substs: ty::ClosureSubsts<'tcx>, + substs: SubstsRef<'tcx>, ) -> ty::PolyTraitRef<'tcx> { debug!( "closure_trait_ref_unnormalized(obligation={:?}, closure_def_id={:?}, substs={:?})", @@ -3863,9 +3919,9 @@ &mut self, obligation: &TraitObligation<'tcx>, closure_def_id: DefId, - substs: ty::GeneratorSubsts<'tcx>, + substs: SubstsRef<'tcx>, ) -> ty::PolyTraitRef<'tcx> { - let gen_sig = substs.poly_sig(closure_def_id, self.tcx()); + let gen_sig = substs.as_generator().poly_sig(closure_def_id, self.tcx()); // (1) Feels icky to skip the binder here, but OTOH we know // that the self-type is an generator type and hence is
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index f0389bb..c1c6eb8 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs
@@ -125,7 +125,7 @@ let trait_def = tcx.trait_def(trait_def_id); let ancestors = trait_def.ancestors(tcx, impl_data.impl_def_id); - match ancestors.defs(tcx, item.ident, item.kind, trait_def_id).next() { + match ancestors.leaf_def(tcx, item.ident, item.kind) { Some(node_item) => { let substs = tcx.infer_ctxt().enter(|infcx| { let param_env = param_env.with_reveal_all(); @@ -419,7 +419,7 @@ // The predicates will contain default bounds like `T: Sized`. We need to // remove these bounds, and add `T: ?Sized` to any untouched type parameters. - let predicates = &tcx.predicates_of(impl_def_id).predicates; + let predicates = tcx.predicates_of(impl_def_id).predicates; let mut pretty_predicates = Vec::with_capacity( predicates.len() + types_without_default_bounds.len());
diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs index b43881d..c64d674 100644 --- a/src/librustc/traits/specialize/specialization_graph.rs +++ b/src/librustc/traits/specialize/specialization_graph.rs
@@ -2,13 +2,11 @@ use crate::hir::def_id::DefId; use crate::ich::{self, StableHashingContext}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, - StableHasherResult}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use crate::traits; use crate::ty::{self, TyCtxt, TypeFoldable}; use crate::ty::fast_reject::{self, SimplifiedType}; use syntax::ast::Ident; -use crate::util::captures::Captures; use crate::util::nodemap::{DefIdMap, FxHashMap}; /// A per-trait graph of impls in specialization order. At the moment, this @@ -85,11 +83,11 @@ /// Insert an impl into this set of children without comparing to any existing impls. fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - if let Some(sty) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { - debug!("insert_blindly: impl_def_id={:?} sty={:?}", impl_def_id, sty); - self.nonblanket_impls.entry(sty).or_default().push(impl_def_id) + if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { + debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st); + self.nonblanket_impls.entry(st).or_default().push(impl_def_id) } else { - debug!("insert_blindly: impl_def_id={:?} sty=None", impl_def_id); + debug!("insert_blindly: impl_def_id={:?} st=None", impl_def_id); self.blanket_impls.push(impl_def_id) } } @@ -100,11 +98,11 @@ fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let vec: &mut Vec<DefId>; - if let Some(sty) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { - debug!("remove_existing: impl_def_id={:?} sty={:?}", impl_def_id, sty); - vec = self.nonblanket_impls.get_mut(&sty).unwrap(); + if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { + debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st); + vec = self.nonblanket_impls.get_mut(&st).unwrap(); } else { - debug!("remove_existing: impl_def_id={:?} sty=None", impl_def_id); + debug!("remove_existing: impl_def_id={:?} st=None", impl_def_id); vec = &mut self.blanket_impls; } @@ -130,7 +128,7 @@ ); let possible_siblings = match simplified_self { - Some(sty) => PotentialSiblings::Filtered(self.filtered(sty)), + Some(st) => PotentialSiblings::Filtered(self.filtered(st)), None => PotentialSiblings::Unfiltered(self.iter()), }; @@ -162,7 +160,6 @@ } }; - let tcx = tcx.global_tcx(); let (le, ge) = traits::overlapping_impls( tcx, possible_sibling, @@ -249,8 +246,8 @@ self.blanket_impls.iter().chain(nonblanket).cloned() } - fn filtered(&mut self, sty: SimplifiedType) -> impl Iterator<Item = DefId> + '_ { - let nonblanket = self.nonblanket_impls.entry(sty).or_default().iter(); + fn filtered(&mut self, st: SimplifiedType) -> impl Iterator<Item = DefId> + '_ { + let nonblanket = self.nonblanket_impls.entry(st).or_default().iter(); self.blanket_impls.iter().chain(nonblanket).cloned() } } @@ -395,7 +392,7 @@ /// The parent of a given impl, which is the `DefId` of the trait when the /// impl is a "specialization root". pub fn parent(&self, child: DefId) -> DefId { - *self.parent.get(&child).unwrap() + *self.parent.get(&child).unwrap_or_else(|| panic!("Failed to get parent for {:?}", child)) } } @@ -421,6 +418,35 @@ tcx.associated_items(self.def_id()) } + /// Finds an associated item defined in this node. + /// + /// If this returns `None`, the item can potentially still be found in + /// parents of this node. + pub fn item( + &self, + tcx: TyCtxt<'tcx>, + trait_item_name: Ident, + trait_item_kind: ty::AssocKind, + trait_def_id: DefId, + ) -> Option<ty::AssocItem> { + use crate::ty::AssocKind::*; + + tcx.associated_items(self.def_id()) + .find(move |impl_item| match (trait_item_kind, impl_item.kind) { + | (Const, Const) + | (Method, Method) + | (Type, Type) + | (Type, OpaqueTy) // assoc. types can be made opaque in impls + => tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id), + + | (Const, _) + | (Method, _) + | (Type, _) + | (OpaqueTy, _) + => false, + }) + } + pub fn def_id(&self) -> DefId { match *self { Node::Impl(did) => did, @@ -429,6 +455,7 @@ } } +#[derive(Copy, Clone)] pub struct Ancestors<'tcx> { trait_def_id: DefId, specialization_graph: &'tcx Graph, @@ -467,32 +494,18 @@ } impl<'tcx> Ancestors<'tcx> { - /// Search the items from the given ancestors, returning each definition - /// with the given name and the given kind. - // FIXME(#35870): avoid closures being unexported due to `impl Trait`. - #[inline] - pub fn defs( - self, + /// Finds the bottom-most (ie. most specialized) definition of an associated + /// item. + pub fn leaf_def( + mut self, tcx: TyCtxt<'tcx>, trait_item_name: Ident, trait_item_kind: ty::AssocKind, - trait_def_id: DefId, - ) -> impl Iterator<Item = NodeItem<ty::AssocItem>> + Captures<'tcx> + 'tcx { - self.flat_map(move |node| { - use crate::ty::AssocKind::*; - node.items(tcx).filter(move |impl_item| match (trait_item_kind, impl_item.kind) { - | (Const, Const) - | (Method, Method) - | (Type, Type) - | (Type, OpaqueTy) - => tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id), - - | (Const, _) - | (Method, _) - | (Type, _) - | (OpaqueTy, _) - => false, - }).map(move |item| NodeItem { node: node, item: item }) + ) -> Option<NodeItem<ty::AssocItem>> { + let trait_def_id = self.trait_def_id; + self.find_map(|node| { + node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) + .map(|item| NodeItem { node, item }) }) } } @@ -513,9 +526,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for Children { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let Children { ref nonblanket_impls, ref blanket_impls,
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 68c9722..109e884 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs
@@ -4,7 +4,7 @@ use crate::traits::project::Normalized; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::{self, Lift, Ty, TyCtxt}; -use syntax::symbol::InternedString; +use syntax::symbol::Symbol; use std::fmt; use std::rc::Rc; @@ -261,11 +261,11 @@ /// for debug output in tests anyway. struct BoundNamesCollector { // Just sort by name because `BoundRegion::BrNamed` does not have a `BoundVar` index anyway. - regions: BTreeSet<InternedString>, + regions: BTreeSet<Symbol>, // Sort by `BoundVar` index, so usually this should be equivalent to the order given // by the list of type parameters. - types: BTreeMap<u32, InternedString>, + types: BTreeMap<u32, Symbol>, binder_index: ty::DebruijnIndex, } @@ -312,14 +312,14 @@ } fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - match t.sty { + match t.kind { ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { self.types.insert( bound_ty.var.as_u32(), match bound_ty.kind { ty::BoundTyKind::Param(name) => name, ty::BoundTyKind::Anon => - InternedString::intern(&format!("^{}", bound_ty.var.as_u32()), + Symbol::intern(&format!("^{}", bound_ty.var.as_u32()), ), } ); @@ -340,7 +340,7 @@ } ty::BoundRegion::BrAnon(var) => { - self.regions.insert(InternedString::intern(&format!("'^{}", var))); + self.regions.insert(Symbol::intern(&format!("'^{}", var))); } _ => (), @@ -481,17 +481,23 @@ .and_then(|r| Some(super::ObjectTypeBound(ty, r))) ), super::ObjectCastObligation(ty) => tcx.lift(&ty).map(super::ObjectCastObligation), + super::Coercion { source, target } => Some(super::Coercion { + source: tcx.lift(&source)?, + target: tcx.lift(&target)?, + }), super::AssignmentLhsSized => Some(super::AssignmentLhsSized), super::TupleInitializerSized => Some(super::TupleInitializerSized), super::StructInitializerSized => Some(super::StructInitializerSized), super::VariableType(id) => Some(super::VariableType(id)), - super::ReturnType(id) => Some(super::ReturnType(id)), + super::ReturnValue(id) => Some(super::ReturnValue(id)), + super::ReturnType => Some(super::ReturnType), super::SizedArgumentType => Some(super::SizedArgumentType), super::SizedReturnType => Some(super::SizedReturnType), super::SizedYieldType => Some(super::SizedYieldType), - super::RepeatVec => Some(super::RepeatVec), + super::RepeatVec(suggest_flag) => Some(super::RepeatVec(suggest_flag)), super::FieldSized { adt_kind, last } => Some(super::FieldSized { adt_kind, last }), super::ConstSized => Some(super::ConstSized), + super::ConstPatternStructural => Some(super::ConstPatternStructural), super::SharedStatic => Some(super::SharedStatic), super::BuiltinDerivedObligation(ref cause) => { tcx.lift(cause).map(super::BuiltinDerivedObligation) @@ -543,6 +549,7 @@ super::MethodReceiver => Some(super::MethodReceiver), super::BlockTailExpression(id) => Some(super::BlockTailExpression(id)), super::TrivialBound => Some(super::TrivialBound), + super::AssocTypeBound(impl_sp, sp) => Some(super::AssocTypeBound(impl_sp, sp)), } } }
diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs index 3e5520d..d8b1eff 100644 --- a/src/librustc/traits/util.rs +++ b/src/librustc/traits/util.rs
@@ -4,10 +4,9 @@ use crate::hir; use crate::hir::def_id::DefId; -use crate::traits::specialize::specialization_graph::NodeItem; use crate::ty::{self, Ty, TyCtxt, ToPredicate, ToPolyTraitRef}; use crate::ty::outlives::Component; -use crate::ty::subst::{Kind, Subst, SubstsRef}; +use crate::ty::subst::{GenericArg, Subst, SubstsRef}; use crate::util::nodemap::FxHashSet; use super::{Obligation, ObligationCause, PredicateObligation, SelectionContext, Normalized}; @@ -551,7 +550,7 @@ trait_def_id: DefId, recursion_depth: usize, self_ty: Ty<'tcx>, - params: &[Kind<'tcx>]) + params: &[GenericArg<'tcx>]) -> PredicateObligation<'tcx> { let trait_ref = ty::TraitRef { @@ -654,22 +653,21 @@ match self.hir().as_local_hir_id(node_item_def_id) { Some(hir_id) => { let item = self.hir().expect_item(hir_id); - if let hir::ItemKind::Impl(_, _, defaultness, ..) = item.node { + if let hir::ItemKind::Impl(_, _, defaultness, ..) = item.kind { defaultness.is_default() } else { false } } None => { - self.global_tcx() - .impl_defaultness(node_item_def_id) + self.impl_defaultness(node_item_def_id) .is_default() } } } - pub fn impl_item_is_final(self, node_item: &NodeItem<hir::Defaultness>) -> bool { - node_item.item.is_final() && !self.impl_is_default(node_item.node.def_id()) + pub fn impl_item_is_final(self, assoc_item: &ty::AssocItem) -> bool { + assoc_item.defaultness.is_final() && !self.impl_is_default(assoc_item.container.id()) } }
diff --git a/src/librustc/ty/_match.rs b/src/librustc/ty/_match.rs index f800a70..a0d2278 100644 --- a/src/librustc/ty/_match.rs +++ b/src/librustc/ty/_match.rs
@@ -59,7 +59,7 @@ a, b); if a == b { return Ok(a); } - match (&a.sty, &b.sty) { + match (&a.kind, &b.kind) { (_, &ty::Infer(ty::FreshTy(_))) | (_, &ty::Infer(ty::FreshIntTy(_))) | (_, &ty::Infer(ty::FreshFloatTy(_))) => {
diff --git a/src/librustc/ty/binding.rs b/src/librustc/ty/binding.rs index 1290141..5570144 100644 --- a/src/librustc/ty/binding.rs +++ b/src/librustc/ty/binding.rs
@@ -2,7 +2,7 @@ use crate::hir::BindingAnnotation; use crate::hir::Mutability; -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] pub enum BindingMode { BindByReference(Mutability), BindByValue(Mutability),
diff --git a/src/librustc/ty/cast.rs b/src/librustc/ty/cast.rs index 7ea5c73..bc12412 100644 --- a/src/librustc/ty/cast.rs +++ b/src/librustc/ty/cast.rs
@@ -52,7 +52,7 @@ /// Returns `Some` for integral/pointer casts. /// casts like unsizing casts will return `None` pub fn from_ty(t: Ty<'tcx>) -> Option<CastTy<'tcx>> { - match t.sty { + match t.kind { ty::Bool => Some(CastTy::Int(IntTy::Bool)), ty::Char => Some(CastTy::Int(IntTy::Char)), ty::Int(_) => Some(CastTy::Int(IntTy::I)),
diff --git a/src/librustc/ty/codec.rs b/src/librustc/ty/codec.rs index 1aa2150..d5e7ac1 100644 --- a/src/librustc/ty/codec.rs +++ b/src/librustc/ty/codec.rs
@@ -13,9 +13,10 @@ use rustc_serialize::{Decodable, Decoder, Encoder, Encodable, opaque}; use std::hash::Hash; use std::intrinsics; -use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{self, List, Ty, TyCtxt}; use crate::ty::subst::SubstsRef; -use crate::mir::interpret::Allocation; +use crate::mir::{self, interpret::Allocation}; +use syntax_pos::Span; /// The shorthand encoding uses an enum's variant index `usize` /// and is offset by this value so it never matches a real variant. @@ -31,7 +32,7 @@ impl<'tcx> EncodableWithShorthand for Ty<'tcx> { type Variant = ty::TyKind<'tcx>; fn variant(&self) -> &Self::Variant { - &self.sty + &self.kind } } @@ -92,16 +93,16 @@ Ok(()) } -pub fn encode_predicates<'tcx, E, C>(encoder: &mut E, - predicates: &ty::GenericPredicates<'tcx>, - cache: C) - -> Result<(), E::Error> +pub fn encode_spanned_predicates<'tcx, E, C>( + encoder: &mut E, + predicates: &'tcx [(ty::Predicate<'tcx>, Span)], + cache: C, +) -> Result<(), E::Error> where E: TyEncoder, C: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap<ty::Predicate<'tcx>, usize>, { - predicates.parent.encode(encoder)?; - predicates.predicates.len().encode(encoder)?; - for (predicate, span) in &predicates.predicates { + predicates.len().encode(encoder)?; + for (predicate, span) in predicates { encode_with_shorthand(encoder, predicate, &cache)?; span.encode(encoder)?; } @@ -182,13 +183,15 @@ } #[inline] -pub fn decode_predicates<D>(decoder: &mut D) -> Result<ty::GenericPredicates<'tcx>, D::Error> +pub fn decode_spanned_predicates<D>( + decoder: &mut D, +) -> Result<&'tcx [(ty::Predicate<'tcx>, Span)], D::Error> where D: TyDecoder<'tcx>, { - Ok(ty::GenericPredicates { - parent: Decodable::decode(decoder)?, - predicates: (0..decoder.read_usize()?).map(|_| { + let tcx = decoder.tcx(); + Ok(tcx.arena.alloc_from_iter( + (0..decoder.read_usize()?).map(|_| { // Handle shorthands first, if we have an usize > 0x80. let predicate = if decoder.positioned_at_shorthand() { let pos = decoder.read_usize()?; @@ -202,7 +205,7 @@ Ok((predicate, Decodable::decode(decoder)?)) }) .collect::<Result<Vec<_>, _>>()?, - }) + )) } #[inline] @@ -216,6 +219,18 @@ } #[inline] +pub fn decode_place<D>(decoder: &mut D) -> Result<mir::Place<'tcx>, D::Error> +where + D: TyDecoder<'tcx>, +{ + let base: mir::PlaceBase<'tcx> = Decodable::decode(decoder)?; + let len = decoder.read_usize()?; + let projection: &'tcx List<mir::PlaceElem<'tcx>> = + decoder.tcx().mk_place_elems((0..len).map(|_| Decodable::decode(decoder)))?; + Ok(mir::Place { base, projection }) +} + +#[inline] pub fn decode_region<D>(decoder: &mut D) -> Result<ty::Region<'tcx>, D::Error> where D: TyDecoder<'tcx>, @@ -339,6 +354,8 @@ use $crate::ty::subst::SubstsRef; use $crate::hir::def_id::{CrateNum}; + use syntax_pos::Span; + use super::$DecoderName; impl<$($typaram ),*> Decoder for $DecoderName<$($typaram),*> { @@ -393,11 +410,11 @@ } } - impl<$($typaram),*> SpecializedDecoder<ty::GenericPredicates<'tcx>> + impl<$($typaram),*> SpecializedDecoder<&'tcx [(ty::Predicate<'tcx>, Span)]> for $DecoderName<$($typaram),*> { fn specialized_decode(&mut self) - -> Result<ty::GenericPredicates<'tcx>, Self::Error> { - decode_predicates(self) + -> Result<&'tcx [(ty::Predicate<'tcx>, Span)], Self::Error> { + decode_spanned_predicates(self) } } @@ -408,6 +425,15 @@ } } + impl<$($typaram),*> SpecializedDecoder<$crate::mir::Place<'tcx>> + for $DecoderName<$($typaram),*> { + fn specialized_decode( + &mut self + ) -> Result<$crate::mir::Place<'tcx>, Self::Error> { + decode_place(self) + } + } + impl<$($typaram),*> SpecializedDecoder<ty::Region<'tcx>> for $DecoderName<$($typaram),*> { fn specialized_decode(&mut self) -> Result<ty::Region<'tcx>, Self::Error> {
diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs index e4022bb..676916f 100644 --- a/src/librustc/ty/constness.rs +++ b/src/librustc/ty/constness.rs
@@ -2,7 +2,7 @@ use crate::hir::def_id::DefId; use crate::hir; use crate::ty::TyCtxt; -use syntax_pos::symbol::{sym, Symbol}; +use syntax_pos::symbol::Symbol; use crate::hir::map::blocks::FnLikeNode; use syntax::attr; @@ -13,14 +13,11 @@ self.is_const_fn_raw(def_id) && match self.is_unstable_const_fn(def_id) { Some(feature_name) => { // has a `rustc_const_unstable` attribute, check whether the user enabled the - // corresponding feature gate, const_constructor is not a lib feature, so has - // to be checked separately. + // corresponding feature gate. self.features() .declared_lib_features .iter() .any(|&(sym, _)| sym == feature_name) - || (feature_name == sym::const_constructor - && self.features().const_constructor) }, // functions without const stability are either stable user written // const fn or the user is using feature gates and we thus don't @@ -31,9 +28,7 @@ /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it pub fn is_unstable_const_fn(self, def_id: DefId) -> Option<Symbol> { - if self.is_constructor(def_id) { - Some(sym::const_constructor) - } else if self.is_const_fn_raw(def_id) { + if self.is_const_fn_raw(def_id) { self.lookup_stability(def_id)?.const_stability } else { None
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 785f45e..3d28bee 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs
@@ -1,6 +1,5 @@ -//! Type context book-keeping. - // ignore-tidy-filelength +//! Type context book-keeping. use crate::arena::Arena; use crate::dep_graph::DepGraph; @@ -23,15 +22,15 @@ use crate::middle::lang_items; use crate::middle::resolve_lifetime::{self, ObjectLifetimeDefault}; use crate::middle::stability; -use crate::mir::{Body, interpret, ProjectionKind, Promoted}; +use crate::mir::{Body, Field, interpret, Local, Place, PlaceElem, ProjectionKind, Promoted}; use crate::mir::interpret::{ConstValue, Allocation, Scalar}; -use crate::ty::subst::{Kind, InternalSubsts, SubstsRef, Subst}; +use crate::ty::subst::{GenericArg, InternalSubsts, SubstsRef, Subst}; use crate::ty::ReprOptions; use crate::traits; use crate::traits::{Clause, Clauses, GoalKind, Goal, Goals}; use crate::ty::{self, DefIdTree, Ty, TypeAndMut}; use crate::ty::{TyS, TyKind, List}; -use crate::ty::{AdtKind, AdtDef, ClosureSubsts, GeneratorSubsts, Region, Const}; +use crate::ty::{AdtKind, AdtDef, Region, Const}; use crate::ty::{PolyFnSig, InferTy, ParamTy, ProjectionTy, ExistentialPredicate, Predicate}; use crate::ty::RegionKind; use crate::ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid, ConstVid}; @@ -41,20 +40,21 @@ use crate::ty::layout::{LayoutDetails, TargetDataLayout, VariantIdx}; use crate::ty::query; use crate::ty::steal::Steal; -use crate::ty::subst::{UserSubsts, UnpackedKind}; +use crate::ty::subst::{UserSubsts, GenericArgKind}; use crate::ty::{BoundVar, BindingMode}; use crate::ty::CanonicalPolyFnSig; use crate::util::common::ErrorReported; -use crate::util::nodemap::{DefIdMap, DefIdSet, ItemLocalMap, ItemLocalSet}; +use crate::util::nodemap::{DefIdMap, DefIdSet, ItemLocalMap, ItemLocalSet, NodeMap}; use crate::util::nodemap::{FxHashMap, FxHashSet}; +use crate::util::profiling::SelfProfilerRef; use errors::DiagnosticBuilder; use arena::SyncDroplessArena; use smallvec::SmallVec; use rustc_data_structures::stable_hasher::{ - HashStable, StableHasher, StableHasherResult, StableVec, hash_stable_hashmap, + HashStable, StableHasher, StableVec, hash_stable_hashmap, }; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_index::vec::{Idx, IndexVec}; use rustc_data_structures::sharded::ShardedHashMap; use rustc_data_structures::sync::{Lrc, Lock, WorkerLocal}; use std::any::Any; @@ -66,7 +66,6 @@ use std::mem; use std::ops::{Deref, Bound}; use std::iter; -use std::sync::mpsc; use std::sync::Arc; use rustc_target::spec::abi; use rustc_macros::HashStable; @@ -74,7 +73,7 @@ use syntax::attr; use syntax::source_map::MultiSpan; use syntax::feature_gate; -use syntax::symbol::{Symbol, InternedString, kw, sym}; +use syntax::symbol::{Symbol, kw, sym}; use syntax_pos::Span; pub struct AllArenas { @@ -108,6 +107,7 @@ goal: InternedSet<'tcx, GoalKind<'tcx>>, goal_list: InternedSet<'tcx, List<Goal<'tcx>>>, projs: InternedSet<'tcx, List<ProjectionKind>>, + place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>, const_: InternedSet<'tcx, Const<'tcx>>, } @@ -126,6 +126,7 @@ goal: Default::default(), goal_list: Default::default(), projs: Default::default(), + place_elems: Default::default(), const_: Default::default(), } } @@ -134,13 +135,13 @@ #[allow(rustc::usage_of_ty_tykind)] #[inline(never)] fn intern_ty(&self, - st: TyKind<'tcx> + kind: TyKind<'tcx> ) -> Ty<'tcx> { - self.type_.intern(st, |st| { - let flags = super::flags::FlagComputation::for_sty(&st); + self.type_.intern(kind, |kind| { + let flags = super::flags::FlagComputation::for_kind(&kind); let ty_struct = TyS { - sty: st, + kind, flags: flags.flags, outer_exclusive_binder: flags.outer_exclusive_binder, }; @@ -150,10 +151,6 @@ } } -pub struct Common<'tcx> { - pub empty_predicates: ty::GenericPredicates<'tcx>, -} - pub struct CommonTypes<'tcx> { pub unit: Ty<'tcx>, pub bool: Ty<'tcx>, @@ -645,7 +642,7 @@ pub fn is_method_call(&self, expr: &hir::Expr) -> bool { // Only paths and method calls/overloaded operators have // entries in type_dependent_defs, ignore the former here. - if let hir::ExprKind::Path(_) = expr.node { + if let hir::ExprKind::Path(_) = expr.kind { return false; } @@ -747,9 +744,7 @@ } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckTables<'tcx> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TypeckTables { local_id_root, ref type_dependent_defs, @@ -823,7 +818,7 @@ } } -newtype_index! { +rustc_index::newtype_index! { pub struct UserTypeAnnotationIndex { derive [HashStable] DEBUG_FORMAT = "UserType({})", @@ -835,7 +830,7 @@ pub type CanonicalUserTypeAnnotations<'tcx> = IndexVec<UserTypeAnnotationIndex, CanonicalUserTypeAnnotation<'tcx>>; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct CanonicalUserTypeAnnotation<'tcx> { pub user_ty: CanonicalUserType<'tcx>, pub span: Span, @@ -871,7 +866,7 @@ user_substs.substs.iter().zip(BoundVar::new(0)..).all(|(kind, cvar)| { match kind.unpack() { - UnpackedKind::Type(ty) => match ty.sty { + GenericArgKind::Type(ty) => match ty.kind { ty::Bound(debruijn, b) => { // We only allow a `ty::INNERMOST` index in substitutions. assert_eq!(debruijn, ty::INNERMOST); @@ -880,7 +875,7 @@ _ => false, }, - UnpackedKind::Lifetime(r) => match r { + GenericArgKind::Lifetime(r) => match r { ty::ReLateBound(debruijn, br) => { // We only allow a `ty::INNERMOST` index in substitutions. assert_eq!(*debruijn, ty::INNERMOST); @@ -889,8 +884,8 @@ _ => false, }, - UnpackedKind::Const(ct) => match ct.val { - ConstValue::Infer(InferConst::Canonical(debruijn, b)) => { + GenericArgKind::Const(ct) => match ct.val { + ConstValue::Bound(debruijn, b) => { // We only allow a `ty::INNERMOST` index in substitutions. assert_eq!(debruijn, ty::INNERMOST); cvar == b @@ -907,7 +902,7 @@ /// A user-given type annotation attached to a constant. These arise /// from constants that are named via paths, like `Foo::<A>::new` and /// so forth. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable)] pub enum UserType<'tcx> { Ty(Ty<'tcx>), @@ -933,7 +928,7 @@ impl<'tcx> CommonTypes<'tcx> { fn new(interners: &CtxtInterners<'tcx>) -> CommonTypes<'tcx> { - let mk = |sty| interners.intern_ty(sty); + let mk = |ty| interners.intern_ty(ty); CommonTypes { unit: mk(Tuple(List::empty())), @@ -957,7 +952,7 @@ f64: mk(Float(ast::FloatTy::F64)), self_param: mk(ty::Param(ty::ParamTy { index: 0, - name: kw::SelfUpper.as_interned_str(), + name: kw::SelfUpper, })), trait_object_dummy_self: mk(Infer(ty::FreshTy(0))), @@ -1017,7 +1012,7 @@ /// /// [rustc guide]: https://rust-lang.github.io/rustc-guide/ty.html #[derive(Copy, Clone)] -#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "TyCtxt")] +#[rustc_diagnostic_item = "TyCtxt"] pub struct TyCtxt<'tcx> { gcx: &'tcx GlobalCtxt<'tcx>, } @@ -1035,14 +1030,15 @@ interners: CtxtInterners<'tcx>, - cstore: &'tcx CrateStoreDyn, + cstore: Box<CrateStoreDyn>, pub sess: &'tcx Session, + pub lint_store: Lrc<lint::LintStore>, + pub dep_graph: DepGraph, - /// Common objects. - pub common: Common<'tcx>, + pub prof: SelfProfilerRef, /// Common types, pre-interned for your convenience. pub types: CommonTypes<'tcx>, @@ -1053,6 +1049,9 @@ /// Common consts, pre-interned for your convenience. pub consts: CommonConsts<'tcx>, + /// Resolutions of `extern crate` items produced by resolver. + extern_crate_map: NodeMap<CrateNum>, + /// Map indicating what traits are in scope for places where this /// is relevant; generated by resolve. trait_map: FxHashMap<DefIndex, @@ -1107,26 +1106,10 @@ layout_interner: ShardedHashMap<&'tcx LayoutDetails, ()>, - /// A general purpose channel to throw data out the back towards LLVM worker - /// threads. - /// - /// This is intended to only get used during the codegen phase of the compiler - /// when satisfying the query for a particular codegen unit. Internally in - /// the query it'll send data along this channel to get processed later. - pub tx_to_llvm_workers: Lock<mpsc::Sender<Box<dyn Any + Send>>>, - output_filenames: Arc<OutputFilenames>, } impl<'tcx> TyCtxt<'tcx> { - /// Gets the global `TyCtxt`. - #[inline] - pub fn global_tcx(self) -> TyCtxt<'tcx> { - TyCtxt { - gcx: self.gcx, - } - } - #[inline(always)] pub fn hir(self) -> &'tcx hir_map::Map<'tcx> { &self.hir_map @@ -1193,7 +1176,7 @@ None => return Bound::Unbounded, }; for meta in attr.meta_item_list().expect("rustc_layout_scalar_valid_range takes args") { - match meta.literal().expect("attribute takes lit").node { + match meta.literal().expect("attribute takes lit").kind { ast::LitKind::Int(a, _) => return Bound::Included(a), _ => span_bug!(attr.span, "rustc_layout_scalar_valid_range expects int arg"), } @@ -1208,70 +1191,52 @@ value.lift_to_tcx(self) } - /// Like lift, but only tries in the global tcx. - pub fn lift_to_global<T: ?Sized + Lift<'tcx>>(self, value: &T) -> Option<T::Lifted> { - value.lift_to_tcx(self.global_tcx()) - } - /// Creates a type context and call the closure with a `TyCtxt` reference /// to the context. The closure enforces that the type context and any interned /// value (types, substs, etc.) can only be used while `ty::tls` has a valid /// reference to the context, to allow formatting values that need it. pub fn create_global_ctxt( s: &'tcx Session, - cstore: &'tcx CrateStoreDyn, + lint_store: Lrc<lint::LintStore>, local_providers: ty::query::Providers<'tcx>, extern_providers: ty::query::Providers<'tcx>, arenas: &'tcx AllArenas, - resolutions: ty::Resolutions, + resolutions: ty::ResolverOutputs, hir: hir_map::Map<'tcx>, on_disk_query_result_cache: query::OnDiskCache<'tcx>, crate_name: &str, - tx: mpsc::Sender<Box<dyn Any + Send>>, output_filenames: &OutputFilenames, ) -> GlobalCtxt<'tcx> { let data_layout = TargetDataLayout::parse(&s.target.target).unwrap_or_else(|err| { s.fatal(&err); }); let interners = CtxtInterners::new(&arenas.interner); - let common = Common { - empty_predicates: ty::GenericPredicates { - parent: None, - predicates: vec![], - }, - }; let common_types = CommonTypes::new(&interners); let common_lifetimes = CommonLifetimes::new(&interners); let common_consts = CommonConsts::new(&interners, &common_types); let dep_graph = hir.dep_graph.clone(); - let max_cnum = cstore.crates_untracked().iter().map(|c| c.as_usize()).max().unwrap_or(0); + let cstore = resolutions.cstore; + let crates = cstore.crates_untracked(); + let max_cnum = crates.iter().map(|c| c.as_usize()).max().unwrap_or(0); let mut providers = IndexVec::from_elem_n(extern_providers, max_cnum + 1); providers[LOCAL_CRATE] = local_providers; let def_path_hash_to_def_id = if s.opts.build_dep_graph() { - let upstream_def_path_tables: Vec<(CrateNum, Lrc<_>)> = cstore - .crates_untracked() + let def_path_tables = crates .iter() .map(|&cnum| (cnum, cstore.def_path_table(cnum))) - .collect(); - - let def_path_tables = || { - upstream_def_path_tables - .iter() - .map(|&(cnum, ref rc)| (cnum, &**rc)) - .chain(iter::once((LOCAL_CRATE, hir.definitions().def_path_table()))) - }; + .chain(iter::once((LOCAL_CRATE, hir.definitions().def_path_table()))); // Precompute the capacity of the hashmap so we don't have to // re-allocate when populating it. - let capacity = def_path_tables().map(|(_, t)| t.size()).sum::<usize>(); + let capacity = def_path_tables.clone().map(|(_, t)| t.size()).sum::<usize>(); let mut map: FxHashMap<_, _> = FxHashMap::with_capacity_and_hasher( capacity, ::std::default::Default::default() ); - for (cnum, def_path_table) in def_path_tables() { + for (cnum, def_path_table) in def_path_tables { def_path_table.add_def_path_hashes_to(cnum, &mut map); } @@ -1289,14 +1254,16 @@ GlobalCtxt { sess: s, + lint_store, cstore, arena: WorkerLocal::new(|_| Arena::default()), interners, dep_graph, - common, + prof: s.prof.clone(), types: common_types, lifetimes: common_lifetimes, consts: common_consts, + extern_crate_map: resolutions.extern_crate_map, trait_map, export_map: resolutions.export_map.into_iter().map(|(k, v)| { let exports: Vec<_> = v.into_iter().map(|e| { @@ -1334,7 +1301,6 @@ stability_interner: Default::default(), allocation_interner: Default::default(), alloc_map: Lock::new(interpret::AllocMap::new()), - tx_to_llvm_workers: Lock::new(tx), output_filenames: Arc::new(output_filenames.clone()), } } @@ -1442,13 +1408,14 @@ } pub fn encode_metadata(self)-> EncodedMetadata { + let _prof_timer = self.prof.generic_activity("generate_crate_metadata"); self.cstore.encode_metadata(self) } // Note that this is *untracked* and should only be used within the query // system if the result is otherwise tracked through queries - pub fn crate_data_as_rc_any(self, cnum: CrateNum) -> Lrc<dyn Any> { - self.cstore.crate_data_as_rc_any(cnum) + pub fn crate_data_as_any(self, cnum: CrateNum) -> &'tcx dyn Any { + self.cstore.crate_data_as_any(cnum) } #[inline(always)] @@ -1458,7 +1425,7 @@ StableHashingContext::new(self.sess, krate, self.hir().definitions(), - self.cstore) + &*self.cstore) } // This method makes sure that we have a DepNode and a Fingerprint for @@ -1486,13 +1453,7 @@ -> Result<(), E::Error> where E: ty::codec::TyEncoder { - self.queries.on_disk_cache.serialize(self.global_tcx(), encoder) - } - - /// If `true`, we should use the AST-based borrowck (we may *also* use - /// the MIR-based borrowck). - pub fn use_ast_borrowck(self) -> bool { - self.borrowck_mode().use_ast() + self.queries.on_disk_cache.serialize(self, encoder) } /// If `true`, we should use the MIR-based borrowck, but also @@ -1554,8 +1515,14 @@ CrateType::Staticlib | CrateType::ProcMacro | CrateType::Cdylib => false, - CrateType::Rlib | + + // FIXME rust-lang/rust#64319, rust-lang/rust#64872: + // We want to block export of generics from dylibs, + // but we must fix rust-lang/rust#65890 before we can + // do that robustly. CrateType::Dylib => true, + + CrateType::Rlib => true, } }) } @@ -1597,7 +1564,7 @@ let hir_id = self.hir().as_local_hir_id(scope_def_id).unwrap(); match self.hir().get(hir_id) { Node::Item(item) => { - match item.node { + match item.kind { ItemKind::Fn(..) => { /* `type_of_def_id()` will work */ } _ => { return None; @@ -1608,7 +1575,7 @@ } let ret_ty = self.type_of(scope_def_id); - match ret_ty.sty { + match ret_ty.kind { ty::FnDef(_, _) => { let sig = ret_ty.fn_sig(*self); let output = self.erase_late_bound_regions(&sig.output()); @@ -1660,7 +1627,7 @@ let tcx = TyCtxt { gcx: self, }; - ty::tls::with_related_context(tcx.global_tcx(), |icx| { + ty::tls::with_related_context(tcx, |icx| { let new_icx = ty::tls::ImplicitCtxt { tcx, query: icx.query.clone(), @@ -1744,7 +1711,7 @@ nop_list_lift!{ProjectionKind => ProjectionKind} // This is the impl for `&'a InternalSubsts<'a>`. -nop_list_lift!{Kind<'a> => Kind<'tcx>} +nop_list_lift!{GenericArg<'a> => GenericArg<'tcx>} pub mod tls { use super::{GlobalCtxt, TyCtxt, ptr_eq}; @@ -2054,7 +2021,7 @@ let shards = tcx.interners.type_.lock_shards(); let types = shards.iter().flat_map(|shard| shard.keys()); for &Interned(t) in types { - let variant = match t.sty { + let variant = match t.kind { ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Str | ty::Never => continue, ty::Error => /* unimportant */ continue, @@ -2123,10 +2090,10 @@ } impl<'tcx, T: 'tcx+?Sized> Copy for Interned<'tcx, T> {} -// N.B., an `Interned<Ty>` compares and hashes as a sty. +// N.B., an `Interned<Ty>` compares and hashes as a `TyKind`. impl<'tcx> PartialEq for Interned<'tcx, TyS<'tcx>> { fn eq(&self, other: &Interned<'tcx, TyS<'tcx>>) -> bool { - self.0.sty == other.0.sty + self.0.kind == other.0.kind } } @@ -2134,14 +2101,14 @@ impl<'tcx> Hash for Interned<'tcx, TyS<'tcx>> { fn hash<H: Hasher>(&self, s: &mut H) { - self.0.sty.hash(s) + self.0.kind.hash(s) } } #[allow(rustc::usage_of_ty_tykind)] impl<'tcx> Borrow<TyKind<'tcx>> for Interned<'tcx, TyS<'tcx>> { fn borrow<'a>(&'a self) -> &'a TyKind<'tcx> { - &self.0.sty + &self.0.kind } } @@ -2172,8 +2139,8 @@ } } -impl<'tcx> Borrow<[Kind<'tcx>]> for Interned<'tcx, InternalSubsts<'tcx>> { - fn borrow<'a>(&'a self) -> &'a [Kind<'tcx>] { +impl<'tcx> Borrow<[GenericArg<'tcx>]> for Interned<'tcx, InternalSubsts<'tcx>> { + fn borrow<'a>(&'a self) -> &'a [GenericArg<'tcx>] { &self.0[..] } } @@ -2185,6 +2152,13 @@ } } +impl<'tcx> Borrow<[PlaceElem<'tcx>]> + for Interned<'tcx, List<PlaceElem<'tcx>>> { + fn borrow(&self) -> &[PlaceElem<'tcx>] { + &self.0[..] + } +} + impl<'tcx> Borrow<RegionKind> for Interned<'tcx, RegionKind> { fn borrow(&self) -> &RegionKind { &self.0 @@ -2229,44 +2203,29 @@ } } -macro_rules! intern_method { - ($lt_tcx:tt, $name:ident: $method:ident($alloc:ty, - $alloc_method:expr, - $alloc_to_key:expr) -> $ty:ty) => { - impl<$lt_tcx> TyCtxt<$lt_tcx> { - pub fn $method(self, v: $alloc) -> &$lt_tcx $ty { - let key = ($alloc_to_key)(&v); - - self.interners.$name.intern_ref(key, || { - Interned($alloc_method(&self.interners.arena, v)) - - }).0 - } - } - } -} - macro_rules! direct_interners { - ($lt_tcx:tt, $($name:ident: $method:ident($ty:ty)),+) => { - $(impl<$lt_tcx> PartialEq for Interned<$lt_tcx, $ty> { + ($($name:ident: $method:ident($ty:ty)),+) => { + $(impl<'tcx> PartialEq for Interned<'tcx, $ty> { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } - impl<$lt_tcx> Eq for Interned<$lt_tcx, $ty> {} + impl<'tcx> Eq for Interned<'tcx, $ty> {} - impl<$lt_tcx> Hash for Interned<$lt_tcx, $ty> { + impl<'tcx> Hash for Interned<'tcx, $ty> { fn hash<H: Hasher>(&self, s: &mut H) { self.0.hash(s) } } - intern_method!( - $lt_tcx, - $name: $method($ty, - |a: &$lt_tcx SyncDroplessArena, v| -> &$lt_tcx $ty { a.alloc(v) }, - |x| x) -> $ty);)+ + impl<'tcx> TyCtxt<'tcx> { + pub fn $method(self, v: $ty) -> &'tcx $ty { + self.interners.$name.intern_ref(&v, || { + Interned(self.interners.arena.alloc(v)) + }).0 + } + })+ } } @@ -2274,7 +2233,7 @@ x.has_type_flags(ty::TypeFlags::KEEP_IN_LOCAL_TCX) } -direct_interners!('tcx, +direct_interners!( region: mk_region(RegionKind), goal: mk_goal(GoalKind<'tcx>), const_: mk_const(Const<'tcx>) @@ -2282,37 +2241,28 @@ macro_rules! slice_interners { ($($field:ident: $method:ident($ty:ty)),+) => ( - $(intern_method!( 'tcx, $field: $method( - &[$ty], - |a, v| List::from_arena(a, v), - Deref::deref) -> List<$ty>);)+ + $(impl<'tcx> TyCtxt<'tcx> { + pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> { + self.interners.$field.intern_ref(v, || { + Interned(List::from_arena(&self.interners.arena, v)) + }).0 + } + })+ ); } slice_interners!( + type_list: _intern_type_list(Ty<'tcx>), + substs: _intern_substs(GenericArg<'tcx>), + canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo), existential_predicates: _intern_existential_predicates(ExistentialPredicate<'tcx>), predicates: _intern_predicates(Predicate<'tcx>), - type_list: _intern_type_list(Ty<'tcx>), - substs: _intern_substs(Kind<'tcx>), clauses: _intern_clauses(Clause<'tcx>), goal_list: _intern_goals(Goal<'tcx>), - projs: _intern_projs(ProjectionKind) + projs: _intern_projs(ProjectionKind), + place_elems: _intern_place_elems(PlaceElem<'tcx>) ); -// This isn't a perfect fit: `CanonicalVarInfo` slices are always -// allocated in the global arena, so this `intern_method!` macro is -// overly general. However, we just return `false` for the code that checks -// whether they belong in the thread-local arena, so no harm done, and -// seems better than open-coding the rest. -intern_method! { - 'tcx, - canonical_var_infos: _intern_canonical_var_infos( - &[CanonicalVarInfo], - |a, v| List::from_arena(a, v), - Deref::deref - ) -> List<CanonicalVarInfo> -} - impl<'tcx> TyCtxt<'tcx> { /// Given a `fn` type, returns an equivalent `unsafe fn` type; /// that is, a `fn` type that is equivalent in every way for being @@ -2335,7 +2285,7 @@ /// It cannot convert a closure that requires unsafe. pub fn coerce_closure_fn_ty(self, sig: PolyFnSig<'tcx>, unsafety: hir::Unsafety) -> Ty<'tcx> { let converted_sig = sig.map_bound(|s| { - let params_iter = match s.inputs()[0].sty { + let params_iter = match s.inputs()[0].kind { ty::Tuple(params) => { params.into_iter().map(|k| k.expect_ty()) } @@ -2485,7 +2435,7 @@ #[inline] pub fn mk_array(self, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> { - self.mk_ty(Array(ty, ty::Const::from_usize(self.global_tcx(), n))) + self.mk_ty(Array(ty, ty::Const::from_usize(self, n))) } #[inline] @@ -2495,13 +2445,13 @@ #[inline] pub fn intern_tup(self, ts: &[Ty<'tcx>]) -> Ty<'tcx> { - let kinds: Vec<_> = ts.into_iter().map(|&t| Kind::from(t)).collect(); + let kinds: Vec<_> = ts.into_iter().map(|&t| GenericArg::from(t)).collect(); self.mk_ty(Tuple(self.intern_substs(&kinds))) } pub fn mk_tup<I: InternAs<[Ty<'tcx>], Ty<'tcx>>>(self, iter: I) -> I::Output { iter.intern_with(|ts| { - let kinds: Vec<_> = ts.into_iter().map(|&t| Kind::from(t)).collect(); + let kinds: Vec<_> = ts.into_iter().map(|&t| GenericArg::from(t)).collect(); self.mk_ty(Tuple(self.intern_substs(&kinds))) }) } @@ -2557,7 +2507,7 @@ } #[inline] - pub fn mk_closure(self, closure_id: DefId, closure_substs: ClosureSubsts<'tcx>) + pub fn mk_closure(self, closure_id: DefId, closure_substs: SubstsRef<'tcx>) -> Ty<'tcx> { self.mk_ty(Closure(closure_id, closure_substs)) } @@ -2565,7 +2515,7 @@ #[inline] pub fn mk_generator(self, id: DefId, - generator_substs: GeneratorSubsts<'tcx>, + generator_substs: SubstsRef<'tcx>, movability: hir::GeneratorMovability) -> Ty<'tcx> { self.mk_ty(Generator(id, generator_substs, movability)) @@ -2617,7 +2567,7 @@ } #[inline] - pub fn mk_ty_param(self, index: u32, name: InternedString) -> Ty<'tcx> { + pub fn mk_ty_param(self, index: u32, name: Symbol) -> Ty<'tcx> { self.mk_ty(Param(ParamTy { index, name: name })) } @@ -2625,7 +2575,7 @@ pub fn mk_const_param( self, index: u32, - name: InternedString, + name: Symbol, ty: Ty<'tcx> ) -> &'tcx Const<'tcx> { self.mk_const(ty::Const { @@ -2635,7 +2585,7 @@ } - pub fn mk_param_from_def(self, param: &ty::GenericParamDef) -> Kind<'tcx> { + pub fn mk_param_from_def(self, param: &ty::GenericParamDef) -> GenericArg<'tcx> { match param.kind { GenericParamDefKind::Lifetime => { self.mk_region(ty::ReEarlyBound(param.to_early_bound_region_data())).into() @@ -2652,6 +2602,48 @@ self.mk_ty(Opaque(def_id, substs)) } + pub fn mk_place_field(self, place: Place<'tcx>, f: Field, ty: Ty<'tcx>) -> Place<'tcx> { + self.mk_place_elem(place, PlaceElem::Field(f, ty)) + } + + pub fn mk_place_deref(self, place: Place<'tcx>) -> Place<'tcx> { + self.mk_place_elem(place, PlaceElem::Deref) + } + + pub fn mk_place_downcast( + self, + place: Place<'tcx>, + adt_def: &'tcx AdtDef, + variant_index: VariantIdx, + ) -> Place<'tcx> { + self.mk_place_elem( + place, + PlaceElem::Downcast(Some(adt_def.variants[variant_index].ident.name), variant_index), + ) + } + + pub fn mk_place_downcast_unnamed( + self, + place: Place<'tcx>, + variant_index: VariantIdx, + ) -> Place<'tcx> { + self.mk_place_elem(place, PlaceElem::Downcast(None, variant_index)) + } + + pub fn mk_place_index(self, place: Place<'tcx>, index: Local) -> Place<'tcx> { + self.mk_place_elem(place, PlaceElem::Index(index)) + } + + /// This method copies `Place`'s projection, add an element and reintern it. Should not be used + /// to build a full `Place` it's just a convenient way to grab a projection and modify it in + /// flight. + pub fn mk_place_elem(self, place: Place<'tcx>, elem: PlaceElem<'tcx>) -> Place<'tcx> { + let mut projection = place.projection.to_vec(); + projection.push(elem); + + Place { base: place.base, projection: self.intern_place_elems(&projection) } + } + pub fn intern_existential_predicates(self, eps: &[ExistentialPredicate<'tcx>]) -> &'tcx List<ExistentialPredicate<'tcx>> { assert!(!eps.is_empty()); @@ -2680,7 +2672,7 @@ } } - pub fn intern_substs(self, ts: &[Kind<'tcx>]) -> &'tcx List<Kind<'tcx>> { + pub fn intern_substs(self, ts: &[GenericArg<'tcx>]) -> &'tcx List<GenericArg<'tcx>> { if ts.len() == 0 { List::empty() } else { @@ -2696,11 +2688,19 @@ } } + pub fn intern_place_elems(self, ts: &[PlaceElem<'tcx>]) -> &'tcx List<PlaceElem<'tcx>> { + if ts.len() == 0 { + List::empty() + } else { + self._intern_place_elems(ts) + } + } + pub fn intern_canonical_var_infos(self, ts: &[CanonicalVarInfo]) -> CanonicalVarInfos<'tcx> { if ts.len() == 0 { List::empty() } else { - self.global_tcx()._intern_canonical_var_infos(ts) + self._intern_canonical_var_infos(ts) } } @@ -2753,14 +2753,19 @@ iter.intern_with(|xs| self.intern_type_list(xs)) } - pub fn mk_substs<I: InternAs<[Kind<'tcx>], - &'tcx List<Kind<'tcx>>>>(self, iter: I) -> I::Output { + pub fn mk_substs<I: InternAs<[GenericArg<'tcx>], + &'tcx List<GenericArg<'tcx>>>>(self, iter: I) -> I::Output { iter.intern_with(|xs| self.intern_substs(xs)) } + pub fn mk_place_elems<I: InternAs<[PlaceElem<'tcx>], + &'tcx List<PlaceElem<'tcx>>>>(self, iter: I) -> I::Output { + iter.intern_with(|xs| self.intern_place_elems(xs)) + } + pub fn mk_substs_trait(self, self_ty: Ty<'tcx>, - rest: &[Kind<'tcx>]) + rest: &[GenericArg<'tcx>]) -> SubstsRef<'tcx> { self.mk_substs(iter::once(self_ty.into()).chain(rest.iter().cloned())) @@ -2923,8 +2928,33 @@ impl<T, R, E> InternIteratorElement<T, R> for Result<T, E> { type Output = Result<R, E>; - fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { - Ok(f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?)) + fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(mut iter: I, f: F) + -> Self::Output { + // This code is hot enough that it's worth specializing for the most + // common length lists, to avoid the overhead of `SmallVec` creation. + // The match arms are in order of frequency. The 1, 2, and 0 cases are + // typically hit in ~95% of cases. We assume that if the upper and + // lower bounds from `size_hint` agree they are correct. + Ok(match iter.size_hint() { + (1, Some(1)) => { + let t0 = iter.next().unwrap()?; + assert!(iter.next().is_none()); + f(&[t0]) + } + (2, Some(2)) => { + let t0 = iter.next().unwrap()?; + let t1 = iter.next().unwrap()?; + assert!(iter.next().is_none()); + f(&[t0, t1]) + } + (0, Some(0)) => { + assert!(iter.next().is_none()); + f(&[]) + } + _ => { + f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?) + } + }) } } @@ -2985,12 +3015,16 @@ }; providers.extern_mod_stmt_cnum = |tcx, id| { let id = tcx.hir().as_local_node_id(id).unwrap(); - tcx.cstore.extern_mod_stmt_cnum_untracked(id) + tcx.extern_crate_map.get(&id).cloned() }; providers.all_crate_nums = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); tcx.arena.alloc_slice(&tcx.cstore.crates_untracked()) }; + providers.crate_host_hash = |tcx, cnum| { + assert_ne!(cnum, LOCAL_CRATE); + tcx.cstore.crate_host_hash_untracked(cnum) + }; providers.postorder_cnums = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); tcx.arena.alloc_slice(&tcx.cstore.postorder_cnums_untracked())
diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index 125c48f..77613b5 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs
@@ -45,13 +45,12 @@ ProjectionMismatched(ExpectedFound<DefId>), ProjectionBoundsLength(ExpectedFound<usize>), ExistentialMismatch(ExpectedFound<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>), - + ObjectUnsafeCoercion(DefId), ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), IntrinsicCast, } -#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] pub enum UnconstrainedNumeric { UnconstrainedFloat, UnconstrainedInt, @@ -179,13 +178,14 @@ IntrinsicCast => { write!(f, "cannot coerce intrinsics to function pointers") } + ObjectUnsafeCoercion(_) => write!(f, "coercion to object-unsafe trait object"), } } } impl<'tcx> ty::TyS<'tcx> { pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { - match self.sty { + match self.kind { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => self.to_string().into(), ty::Tuple(ref tys) if tys.is_empty() => self.to_string().into(), @@ -193,7 +193,7 @@ ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.def_path_str(def.did)).into(), ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(), ty::Array(_, n) => { - let n = tcx.lift_to_global(&n).unwrap(); + let n = tcx.lift(&n).unwrap(); match n.try_eval_usize(tcx, ty::ParamEnv::empty()) { Some(n) => { format!("array of {} element{}", n, pluralise!(n)).into() @@ -275,7 +275,7 @@ `.await`ing on both of them"); } } - match (&values.expected.sty, &values.found.sty) { + match (&values.expected.kind, &values.found.kind) { (ty::Float(_), ty::Infer(ty::IntVar(_))) => if let Ok( // Issue #53280 snippet, ) = self.sess.source_map().span_to_snippet(sp) { @@ -373,9 +373,9 @@ debug!( "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", values.expected, - values.expected.sty, + values.expected.kind, values.found, - values.found.sty, + values.found.kind, ); }, CyclicTy(ty) => {
diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc/ty/fast_reject.rs index ee0d33d..27a09b3 100644 --- a/src/librustc/ty/fast_reject.rs +++ b/src/librustc/ty/fast_reject.rs
@@ -1,7 +1,6 @@ use crate::hir::def_id::DefId; use crate::ich::StableHashingContext; -use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, - HashStable}; +use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use std::fmt::Debug; use std::hash::Hash; use std::mem; @@ -20,7 +19,7 @@ /// the non-stable but fast to construct DefId-version is the better choice. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, RustcEncodable, RustcDecodable)] pub enum SimplifiedTypeGen<D> - where D: Copy + Debug + Ord + Eq + Hash + where D: Copy + Debug + Ord + Eq { BoolSimplifiedType, CharSimplifiedType, @@ -60,7 +59,7 @@ ty: Ty<'_>, can_simplify_params: bool, ) -> Option<SimplifiedType> { - match ty.sty { + match ty.kind { ty::Bool => Some(BoolSimplifiedType), ty::Char => Some(CharSimplifiedType), ty::Int(int_type) => Some(IntSimplifiedType(int_type)), @@ -124,10 +123,10 @@ } } -impl<D: Copy + Debug + Ord + Eq + Hash> SimplifiedTypeGen<D> { +impl<D: Copy + Debug + Ord + Eq> SimplifiedTypeGen<D> { pub fn map_def<U, F>(self, map: F) -> SimplifiedTypeGen<U> where F: Fn(D) -> U, - U: Copy + Debug + Ord + Eq + Hash, + U: Copy + Debug + Ord + Eq, { match self { BoolSimplifiedType => BoolSimplifiedType, @@ -156,11 +155,9 @@ impl<'a, D> HashStable<StableHashingContext<'a>> for SimplifiedTypeGen<D> where - D: Copy + Debug + Ord + Eq + Hash + HashStable<StableHashingContext<'a>>, + D: Copy + Debug + Ord + Eq + HashStable<StableHashingContext<'a>>, { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { BoolSimplifiedType |
diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index b2d74f9..d4b7f37 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs
@@ -1,4 +1,4 @@ -use crate::ty::subst::{SubstsRef, UnpackedKind}; +use crate::ty::subst::{SubstsRef, GenericArgKind}; use crate::ty::{self, Ty, TypeFlags, InferConst}; use crate::mir::interpret::ConstValue; @@ -19,9 +19,9 @@ } #[allow(rustc::usage_of_ty_tykind)] - pub fn for_sty(st: &ty::TyKind<'_>) -> FlagComputation { + pub fn for_kind(kind: &ty::TyKind<'_>) -> FlagComputation { let mut result = FlagComputation::new(); - result.add_sty(st); + result.add_kind(kind); result } @@ -63,8 +63,8 @@ } #[allow(rustc::usage_of_ty_tykind)] - fn add_sty(&mut self, st: &ty::TyKind<'_>) { - match st { + fn add_kind(&mut self, kind: &ty::TyKind<'_>) { + match kind { &ty::Bool | &ty::Char | &ty::Int(_) | @@ -94,7 +94,7 @@ &ty::Generator(_, ref substs, _) => { self.add_flags(TypeFlags::HAS_TY_CLOSURE); self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES); - self.add_substs(&substs.substs); + self.add_substs(substs); } &ty::GeneratorWitness(ref ts) => { @@ -106,7 +106,7 @@ &ty::Closure(_, ref substs) => { self.add_flags(TypeFlags::HAS_TY_CLOSURE); self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES); - self.add_substs(&substs.substs); + self.add_substs(substs); } &ty::Bound(debruijn, _) => { @@ -114,6 +114,7 @@ } &ty::Placeholder(..) => { + self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES); self.add_flags(TypeFlags::HAS_TY_PLACEHOLDER); } @@ -123,8 +124,7 @@ match infer { ty::FreshTy(_) | ty::FreshIntTy(_) | - ty::FreshFloatTy(_) => { - } + ty::FreshFloatTy(_) => {} ty::TyVar(_) | ty::IntVar(_) | @@ -240,17 +240,21 @@ self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES | TypeFlags::HAS_CT_INFER); match infer { InferConst::Fresh(_) => {} - InferConst::Canonical(debruijn, _) => self.add_binder(debruijn), InferConst::Var(_) => self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX), } } + ConstValue::Bound(debruijn, _) => self.add_binder(debruijn), ConstValue::Param(_) => { - self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES | TypeFlags::HAS_PARAMS); + self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES); + self.add_flags(TypeFlags::HAS_PARAMS); } ConstValue::Placeholder(_) => { - self.add_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_CT_PLACEHOLDER); + self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES); + self.add_flags(TypeFlags::HAS_CT_PLACEHOLDER); } - _ => {}, + ConstValue::Scalar(_) => {} + ConstValue::Slice { .. } => {} + ConstValue::ByRef { .. } => {} } } @@ -266,9 +270,9 @@ fn add_substs(&mut self, substs: SubstsRef<'_>) { for kind in substs { match kind.unpack() { - UnpackedKind::Type(ty) => self.add_ty(ty), - UnpackedKind::Lifetime(lt) => self.add_region(lt), - UnpackedKind::Const(ct) => self.add_const(ct), + GenericArgKind::Type(ty) => self.add_ty(ty), + GenericArgKind::Lifetime(lt) => self.add_region(lt), + GenericArgKind::Const(ct) => self.add_const(ct), } } }
diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 1e08ae4..bacf3d4 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs
@@ -88,6 +88,9 @@ fn has_infer_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_INFER) } + fn has_infer_consts(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_CT_INFER) + } fn has_local_value(&self) -> bool { self.has_type_flags(TypeFlags::KEEP_IN_LOCAL_TCX) } @@ -472,7 +475,7 @@ } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.sty { + match t.kind { ty::Bound(debruijn, bound_ty) => { if debruijn == self.current_index { let fld_t = &mut self.fld_t; @@ -518,10 +521,7 @@ } fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if let ty::Const { - val: ConstValue::Infer(ty::InferConst::Canonical(debruijn, bound_const)), - ty, - } = *ct { + if let ty::Const { val: ConstValue::Bound(debruijn, bound_const), ty } = *ct { if debruijn == self.current_index { let fld_c = &mut self.fld_c; let ct = fld_c(bound_const, ty); @@ -567,7 +567,10 @@ // identity for bound types and consts let fld_t = |bound_ty| self.mk_ty(ty::Bound(ty::INNERMOST, bound_ty)); let fld_c = |bound_ct, ty| { - self.mk_const_infer(ty::InferConst::Canonical(ty::INNERMOST, bound_ct), ty) + self.mk_const(ty::Const { + val: ConstValue::Bound(ty::INNERMOST, bound_ct), + ty, + }) }; self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t, fld_c) } @@ -718,7 +721,6 @@ // vars. See comment on `shift_vars_through_binders` method in // `subst.rs` for more details. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] enum Direction { In, Out, @@ -776,7 +778,7 @@ } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.sty { + match ty.kind { ty::Bound(debruijn, bound_ty) => { if self.amount == 0 || debruijn < self.current_index { ty @@ -799,10 +801,7 @@ } fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if let ty::Const { - val: ConstValue::Infer(ty::InferConst::Canonical(debruijn, bound_const)), - ty, - } = *ct { + if let ty::Const { val: ConstValue::Bound(debruijn, bound_ct), ty } = *ct { if self.amount == 0 || debruijn < self.current_index { ct } else { @@ -813,7 +812,10 @@ debruijn.shifted_out(self.amount) } }; - self.tcx.mk_const_infer(ty::InferConst::Canonical(debruijn, bound_const), ty) + self.tcx.mk_const(ty::Const { + val: ConstValue::Bound(debruijn, bound_ct), + ty, + }) } } else { ct.super_fold_with(self) @@ -911,13 +913,14 @@ } fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> bool { - if let ty::Const { - val: ConstValue::Infer(ty::InferConst::Canonical(debruijn, _)), - .. - } = *ct { - debruijn >= self.outer_index - } else { - false + // we don't have a `visit_infer_const` callback, so we have to + // hook in here to catch this case (annoying...), but + // otherwise we do want to remember to visit the rest of the + // const, as it has types/regions embedded in a lot of other + // places. + match ct.val { + ConstValue::Bound(debruijn, _) if debruijn >= self.outer_index => true, + _ => ct.super_visit_with(self), } } } @@ -985,7 +988,7 @@ // ignore the inputs to a projection, as they may not appear // in the normalized form if self.just_constrained { - match t.sty { + match t.kind { ty::Projection(..) | ty::Opaque(..) => { return false; } _ => { } }
diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs index 1a0e351..bc0cf4d 100644 --- a/src/librustc/ty/inhabitedness/mod.rs +++ b/src/librustc/ty/inhabitedness/mod.rs
@@ -181,7 +181,7 @@ impl<'tcx> TyS<'tcx> { /// Calculates the forest of `DefId`s from which this type is visibly uninhabited. fn uninhabited_from(&self, tcx: TyCtxt<'tcx>) -> DefIdForest { - match self.sty { + match self.kind { Adt(def, substs) => def.uninhabited_from(tcx, substs), Never => DefIdForest::full(tcx),
diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index a26fa72..5139c80 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs
@@ -1,3 +1,4 @@ +use crate::hir::CodegenFnAttrFlags; use crate::hir::Unsafety; use crate::hir::def::Namespace; use crate::hir::def_id::DefId; @@ -25,6 +26,14 @@ /// `<T as Trait>::method` where `method` receives unsizeable `self: Self`. VtableShim(DefId), + /// `fn()` pointer where the function itself cannot be turned into a pointer. + /// + /// One example in the compiler today is functions annotated with `#[track_caller]`, which + /// must have their implicit caller location argument populated for a call. Because this is a + /// required part of the function's ABI but can't be tracked as a property of the function + /// pointer, we create a single "caller location" at the site where the function is reified. + ReifyShim(DefId), + /// `<fn() as FnTrait>::call_*` /// `DefId` is `FnTrait::call_*` FnPtrShim(DefId, Ty<'tcx>), @@ -54,12 +63,12 @@ fn fn_sig_noadjust(&self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> { let ty = self.ty(tcx); - match ty.sty { + match ty.kind { ty::FnDef(..) | // Shims currently have type FnPtr. Not sure this should remain. ty::FnPtr(_) => ty.fn_sig(tcx), ty::Closure(def_id, substs) => { - let sig = substs.closure_sig(def_id, tcx); + let sig = substs.as_closure().sig(def_id, tcx); let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); sig.map_bound(|sig| tcx.mk_fn_sig( @@ -71,7 +80,7 @@ )) } ty::Generator(def_id, substs, _) => { - let sig = substs.poly_sig(def_id, tcx); + let sig = substs.as_generator().poly_sig(def_id, tcx); let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); @@ -123,6 +132,7 @@ match *self { InstanceDef::Item(def_id) | InstanceDef::VtableShim(def_id) | + InstanceDef::ReifyShim(def_id) | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id, ) | @@ -178,6 +188,9 @@ InstanceDef::VtableShim(_) => { write!(f, " - shim(vtable)") } + InstanceDef::ReifyShim(_) => { + write!(f, " - shim(reify)") + } InstanceDef::Intrinsic(_) => { write!(f, " - intrinsic") } @@ -210,7 +223,7 @@ } pub fn mono(tcx: TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> { - Instance::new(def_id, tcx.global_tcx().empty_substs_for_def_id(def_id)) + Instance::new(def_id, tcx.empty_substs_for_def_id(def_id)) } #[inline] @@ -255,7 +268,7 @@ &ty, ); - let def = match item_type.sty { + let def = match item_type.kind { ty::FnDef(..) if { let f = item_type.fn_sig(tcx); f.abi() == Abi::RustIntrinsic || @@ -290,6 +303,30 @@ result } + pub fn resolve_for_fn_ptr( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Option<Instance<'tcx>> { + debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); + Instance::resolve(tcx, param_env, def_id, substs).map(|resolved| { + let has_track_caller = |def| tcx.codegen_fn_attrs(def).flags + .contains(CodegenFnAttrFlags::TRACK_CALLER); + + match resolved.def { + InstanceDef::Item(def_id) if has_track_caller(def_id) => { + debug!(" => fn pointer created for function with #[track_caller]"); + Instance { + def: InstanceDef::ReifyShim(def_id), + substs, + } + }, + _ => resolved, + } + }) + } + pub fn resolve_for_vtable( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -315,14 +352,14 @@ pub fn resolve_closure( tcx: TyCtxt<'tcx>, def_id: DefId, - substs: ty::ClosureSubsts<'tcx>, + substs: ty::SubstsRef<'tcx>, requested_kind: ty::ClosureKind, ) -> Instance<'tcx> { - let actual_kind = substs.closure_kind(def_id, tcx); + let actual_kind = substs.as_closure().kind(def_id, tcx); match needs_fn_once_adapter_shim(actual_kind, requested_kind) { Ok(true) => Instance::fn_once_adapter_instance(tcx, def_id, substs), - _ => Instance::new(def_id, substs.substs) + _ => Instance::new(def_id, substs) } } @@ -335,7 +372,7 @@ pub fn fn_once_adapter_instance( tcx: TyCtxt<'tcx>, closure_did: DefId, - substs: ty::ClosureSubsts<'tcx>, + substs: ty::SubstsRef<'tcx>, ) -> Instance<'tcx> { debug!("fn_once_adapter_shim({:?}, {:?})", closure_did, @@ -348,7 +385,7 @@ let self_ty = tcx.mk_closure(closure_did, substs); - let sig = substs.closure_sig(closure_did, tcx); + let sig = substs.as_closure().sig(closure_did, tcx); let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); assert_eq!(sig.inputs().len(), 1); let substs = tcx.mk_substs_trait(self_ty, &[sig.inputs()[0].into()]); @@ -395,7 +432,7 @@ traits::VtableGenerator(generator_data) => { Some(Instance { def: ty::InstanceDef::Item(generator_data.generator_def_id), - substs: generator_data.substs.substs + substs: generator_data.substs }) } traits::VtableClosure(closure_data) => {
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index e52feea..e82232a 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs
@@ -1,5 +1,5 @@ use crate::session::{self, DataTypeKind}; -use crate::ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions}; +use crate::ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions, subst::SubstsRef}; use syntax::ast::{self, Ident, IntTy, UintTy}; use syntax::attr; @@ -15,17 +15,15 @@ use crate::hir; use crate::ich::StableHashingContext; use crate::mir::{GeneratorLayout, GeneratorSavedLocal}; -use crate::ty::GeneratorSubsts; use crate::ty::subst::Subst; -use rustc_data_structures::bit_set::BitSet; -use rustc_data_structures::indexed_vec::{IndexVec, Idx}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, - StableHasherResult}; +use rustc_index::bit_set::BitSet; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_index::vec::{IndexVec, Idx}; pub use rustc_target::abi::*; use rustc_target::spec::{HasTargetSpec, abi::Abi as SpecAbi}; use rustc_target::abi::call::{ - ArgAttribute, ArgAttributes, ArgType, Conv, FnType, IgnoreMode, PassMode, Reg, RegKind + ArgAttribute, ArgAttributes, ArgType, Conv, FnType, PassMode, Reg, RegKind }; pub trait IntegerExt { @@ -520,7 +518,7 @@ }; debug_assert!(!ty.has_infer_types()); - Ok(match ty.sty { + Ok(match ty.kind { // Basic scalars. ty::Bool => { tcx.intern_layout(LayoutDetails::scalar(self, Scalar { @@ -573,7 +571,7 @@ } let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); - let metadata = match unsized_part.sty { + let metadata = match unsized_part.kind { ty::Foreign(..) => { return Ok(tcx.intern_layout(LayoutDetails::scalar(self, data_ptr))); } @@ -672,10 +670,10 @@ tcx.intern_layout(unit) } - ty::Generator(def_id, substs, _) => self.generator_layout(ty, def_id, &substs)?, + ty::Generator(def_id, substs, _) => self.generator_layout(ty, def_id, substs)?, ty::Closure(def_id, ref substs) => { - let tys = substs.upvar_tys(def_id, tcx); + let tys = substs.as_closure().upvar_tys(def_id, tcx); univariant(&tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?, &ReprOptions::default(), StructKind::AlwaysSized)? @@ -826,10 +824,14 @@ }); (present_variants.next(), present_variants.next()) }; - if present_first.is_none() { + let present_first = match present_first { + present_first @ Some(_) => present_first, // Uninhabited because it has no variants, or only absent ones. - return tcx.layout_raw(param_env.and(tcx.types.never)); - } + None if def.is_enum() => return tcx.layout_raw(param_env.and(tcx.types.never)), + // if it's a struct, still compute a layout so that we can still compute the + // field offsets + None => Some(VariantIdx::new(0)), + }; let is_struct = !def.is_enum() || // Only one variant is present. @@ -1407,12 +1409,12 @@ &self, ty: Ty<'tcx>, def_id: hir::def_id::DefId, - substs: &GeneratorSubsts<'tcx>, + substs: SubstsRef<'tcx>, ) -> Result<&'tcx LayoutDetails, LayoutError<'tcx>> { use SavedLocalEligibility::*; let tcx = self.tcx; - let subst_field = |ty: Ty<'tcx>| { ty.subst(tcx, substs.substs) }; + let subst_field = |ty: Ty<'tcx>| { ty.subst(tcx, substs) }; let info = tcx.generator_layout(def_id); let (ineligible_locals, assignments) = self.generator_saved_local_eligibility(&info); @@ -1420,9 +1422,9 @@ // Build a prefix layout, including "promoting" all ineligible // locals as part of the prefix. We compute the layout of all of // these fields at once to get optimal packing. - let discr_index = substs.prefix_tys(def_id, tcx).count(); + let discr_index = substs.as_generator().prefix_tys(def_id, tcx).count(); // FIXME(eddyb) set the correct vaidity range for the discriminant. - let discr_layout = self.layout_of(substs.discr_ty(tcx))?; + let discr_layout = self.layout_of(substs.as_generator().discr_ty(tcx))?; let discr = match &discr_layout.abi { Abi::Scalar(s) => s.clone(), _ => bug!(), @@ -1431,7 +1433,7 @@ .map(|local| subst_field(info.field_tys[local])) .map(|ty| tcx.mk_maybe_uninit(ty)) .map(|ty| self.layout_of(ty)); - let prefix_layouts = substs.prefix_tys(def_id, tcx) + let prefix_layouts = substs.as_generator().prefix_tys(def_id, tcx) .map(|ty| self.layout_of(ty)) .chain(iter::once(Ok(discr_layout))) .chain(promoted_layouts) @@ -1618,7 +1620,7 @@ variants); }; - let adt_def = match layout.ty.sty { + let adt_def = match layout.ty.kind { ty::Adt(ref adt_def, _) => { debug!("print-type-size t: `{:?}` process adt", layout.ty); adt_def @@ -1759,12 +1761,12 @@ Err(err) => err }; - match ty.sty { + match ty.kind { ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { let non_zero = !ty.is_unsafe_ptr(); let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); - match tail.sty { + match tail.kind { ty::Param(_) | ty::Projection(_) => { debug_assert!(tail.has_param_types()); Ok(SizeSkeleton::Pointer { @@ -1883,7 +1885,7 @@ impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { - self.global_tcx() + *self } } @@ -2003,7 +2005,7 @@ pub fn layout_of(self, param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> Result<TyLayout<'tcx>, LayoutError<'tcx>> { let cx = LayoutCx { - tcx: self.global_tcx(), + tcx: self, param_env: param_env_and_ty.param_env }; cx.layout_of(param_env_and_ty.value) @@ -2017,7 +2019,7 @@ pub fn layout_of(self, param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> Result<TyLayout<'tcx>, LayoutError<'tcx>> { let cx = LayoutCx { - tcx: self.global_tcx().at(self.span), + tcx: self.at(self.span), param_env: param_env_and_ty.param_env }; cx.layout_of(param_env_and_ty.value) @@ -2040,7 +2042,7 @@ assert_eq!(layout.variants, Variants::Single { index }); } - let fields = match this.ty.sty { + let fields = match this.ty.kind { ty::Adt(def, _) => def.variants[variant_index].fields.len(), _ => bug!() }; @@ -2078,7 +2080,7 @@ })) }; - cx.layout_of(match this.ty.sty { + cx.layout_of(match this.ty.kind { ty::Bool | ty::Char | ty::Int(_) | @@ -2115,7 +2117,7 @@ })); } - match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).sty { + match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind { ty::Slice(_) | ty::Str => tcx.types.usize, ty::Dynamic(_, _) => { @@ -2148,13 +2150,13 @@ // Tuples, generators and closures. ty::Closure(def_id, ref substs) => { - substs.upvar_tys(def_id, tcx).nth(i).unwrap() + substs.as_closure().upvar_tys(def_id, tcx).nth(i).unwrap() } ty::Generator(def_id, ref substs, _) => { match this.variants { Variants::Single { index } => { - substs.state_tys(def_id, tcx) + substs.as_generator().state_tys(def_id, tcx) .nth(index.as_usize()).unwrap() .nth(i).unwrap() } @@ -2162,7 +2164,7 @@ if i == discr_index { return discr_layout(discr); } - substs.prefix_tys(def_id, tcx).nth(i).unwrap() + substs.as_generator().prefix_tys(def_id, tcx).nth(i).unwrap() } } } @@ -2202,7 +2204,7 @@ cx: &C, offset: Size, ) -> Option<PointeeInfo> { - match this.ty.sty { + match this.ty.kind { ty::RawPtr(mt) if offset.bytes() == 0 => { cx.layout_of(mt.ty).to_result().ok() .map(|layout| PointeeInfo { @@ -2309,7 +2311,7 @@ // FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`. if let Some(ref mut pointee) = result { - if let ty::Adt(def, _) = this.ty.sty { + if let ty::Adt(def, _) = this.ty.kind { if def.is_box() && offset.bytes() == 0 { pointee.safe = Some(PointerKind::UniqueOwned); } @@ -2323,9 +2325,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for Variants { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use crate::ty::layout::Variants::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2349,9 +2349,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for DiscriminantKind { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use crate::ty::layout::DiscriminantKind::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2372,9 +2370,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for FieldPlacement { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use crate::ty::layout::FieldPlacement::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2395,19 +2391,13 @@ } impl<'a> HashStable<StableHashingContext<'a>> for VariantIdx { - fn hash_stable<W: StableHasherResult>( - &self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>, - ) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.as_u32().hash_stable(hcx, hasher) } } impl<'a> HashStable<StableHashingContext<'a>> for Abi { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use crate::ty::layout::Abi::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2432,9 +2422,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for Scalar { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let Scalar { value, ref valid_range } = *self; value.hash_stable(hcx, hasher); valid_range.start().hash_stable(hcx, hasher); @@ -2476,29 +2464,19 @@ }); impl<'tcx> HashStable<StableHashingContext<'tcx>> for Align { - fn hash_stable<W: StableHasherResult>( - &self, - hcx: &mut StableHashingContext<'tcx>, - hasher: &mut StableHasher<W>, - ) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { self.bytes().hash_stable(hcx, hasher); } } impl<'tcx> HashStable<StableHashingContext<'tcx>> for Size { - fn hash_stable<W: StableHasherResult>( - &self, - hcx: &mut StableHashingContext<'tcx>, - hasher: &mut StableHasher<W>, - ) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { self.bytes().hash_stable(hcx, hasher); } } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for LayoutError<'tcx> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use crate::ty::layout::LayoutError::*; mem::discriminant(self).hash_stable(hcx, hasher); @@ -2618,6 +2596,7 @@ // It's the ABI's job to select this, not ours. System => bug!("system abi should be selected elsewhere"), + EfiApi => bug!("eficall abi should be selected elsewhere"), Stdcall => Conv::X86Stdcall, Fastcall => Conv::X86Fastcall, @@ -2641,7 +2620,7 @@ let extra_args = if sig.abi == RustCall { assert!(!sig.c_variadic && extra_args.is_empty()); - match sig.inputs().last().unwrap().sty { + match sig.inputs().last().unwrap().kind { ty::Tuple(tupled_arguments) => { inputs = &sig.inputs()[0..sig.inputs().len() - 1]; tupled_arguments.iter().map(|k| k.expect_ty()).collect() @@ -2722,14 +2701,6 @@ } }; - // Store the index of the last argument. This is useful for working with - // C-compatible variadic arguments. - let last_arg_idx = if sig.inputs().is_empty() { - None - } else { - Some(sig.inputs().len() - 1) - }; - let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| { let is_return = arg_idx.is_none(); let mut arg = mk_arg_type(ty, arg_idx); @@ -2739,30 +2710,7 @@ // The same is true for s390x-unknown-linux-gnu // and sparc64-unknown-linux-gnu. if is_return || rust_abi || (!win_x64_gnu && !linux_s390x && !linux_sparc64) { - arg.mode = PassMode::Ignore(IgnoreMode::Zst); - } - } - - // If this is a C-variadic function, this is not the return value, - // and there is one or more fixed arguments; ensure that the `VaListImpl` - // is ignored as an argument. - if sig.c_variadic { - match (last_arg_idx, arg_idx) { - (Some(last_idx), Some(cur_idx)) if last_idx == cur_idx => { - let va_list_did = match cx.tcx().lang_items().va_list() { - Some(did) => did, - None => bug!("`va_list` lang item required for C-variadic functions"), - }; - match ty.sty { - ty::Adt(def, _) if def.did == va_list_did => { - // This is the "spoofed" `VaListImpl`. Set the arguments mode - // so that it will be ignored. - arg.mode = PassMode::Ignore(IgnoreMode::CVarArgs); - } - _ => (), - } - } - _ => {} + arg.mode = PassMode::Ignore; } }
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index f879693..60028f2 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs
@@ -4,7 +4,7 @@ pub use self::AssocItemContainer::*; pub use self::BorrowKind::*; pub use self::IntVarValue::*; -pub use self::fold::TypeFoldable; +pub use self::fold::{TypeFoldable, TypeVisitor}; use crate::hir::{map as hir_map, GlobMap, TraitMap}; use crate::hir::Node; @@ -15,6 +15,7 @@ use crate::ich::Fingerprint; use crate::ich::StableHashingContext; use crate::infer::canonical::Canonical; +use crate::middle::cstore::CrateStoreDyn; use crate::middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem}; use crate::middle::resolve_lifetime::ObjectLifetimeDefault; use crate::mir::Body; @@ -28,7 +29,7 @@ use crate::ty::util::{IntTypeExt, Discr}; use crate::ty::walk::TypeWalker; use crate::util::captures::Captures; -use crate::util::nodemap::{NodeSet, DefIdMap, FxHashMap}; +use crate::util::nodemap::{NodeMap, NodeSet, DefIdMap, FxHashMap}; use arena::SyncDroplessArena; use crate::session::DataTypeKind; @@ -45,15 +46,14 @@ use std::ops::Range; use syntax::ast::{self, Name, Ident, NodeId}; use syntax::attr; -use syntax::ext::hygiene::ExpnId; -use syntax::symbol::{kw, sym, Symbol, InternedString}; +use syntax_pos::symbol::{kw, sym, Symbol}; +use syntax_pos::hygiene::ExpnId; use syntax_pos::Span; use smallvec; -use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, - HashStable}; +use rustc_data_structures::fx::{FxIndexMap}; +use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; +use rustc_index::vec::{Idx, IndexVec}; use crate::hir; @@ -84,6 +84,10 @@ pub use self::instance::{Instance, InstanceDef}; +pub use self::structural_match::search_for_structural_match_violation; +pub use self::structural_match::type_marked_structural; +pub use self::structural_match::NonStructuralMatchTy; + pub use self::trait_def::TraitDef; pub use self::query::queries; @@ -116,12 +120,15 @@ mod context; mod instance; mod structural_impls; +mod structural_match; mod sty; // Data types -#[derive(Clone)] -pub struct Resolutions { +pub struct ResolverOutputs { + pub definitions: hir_map::Definitions, + pub cstore: Box<CrateStoreDyn>, + pub extern_crate_map: NodeMap<CrateNum>, pub trait_map: TraitMap, pub maybe_unused_trait_imports: NodeSet, pub maybe_unused_extern_crates: Vec<(NodeId, Span)>, @@ -159,7 +166,7 @@ /// The "header" of an impl is everything outside the body: a Self type, a trait /// ref (in the case of a trait impl), and a set of predicates (from the /// bounds / where-clauses). -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Debug)] pub struct ImplHeader<'tcx> { pub impl_def_id: DefId, pub self_ty: Ty<'tcx>, @@ -167,6 +174,19 @@ pub predicates: Vec<Predicate<'tcx>>, } +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] +pub enum ImplPolarity { + /// `impl Trait for Type` + Positive, + /// `impl !Trait for Type` + Negative, + /// `#[rustc_reservation_impl] impl Trait for Type` + /// + /// This is a "stability hack", not a real Rust feature. + /// See #64631 for details. + Reservation, +} + #[derive(Copy, Clone, Debug, PartialEq, HashStable)] pub struct AssocItem { pub def_id: DefId, @@ -182,7 +202,7 @@ pub method_has_self_argument: bool, } -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, Debug, HashStable)] pub enum AssocKind { Const, Method, @@ -318,7 +338,7 @@ } } -#[derive(Copy, Clone, PartialEq, Eq, RustcDecodable, RustcEncodable, Hash, HashStable)] +#[derive(Copy, Clone, PartialEq, RustcDecodable, RustcEncodable, HashStable)] pub enum Variance { Covariant, // T<A> <: T<B> iff A <: B -- e.g., function return type Invariant, // T<A> <: T<B> iff B == A -- e.g., type of mutable cell @@ -438,7 +458,7 @@ /// `true` if there are "names" of types and regions and so forth /// that are local to a particular fn - const HAS_FREE_LOCAL_NAMES = 1 << 9; + const HAS_FREE_LOCAL_NAMES = 1 << 9; /// Present if the type belongs in a local type context. /// Only set for Infer other than Fresh. @@ -446,11 +466,11 @@ /// Does this have any `ReLateBound` regions? Used to check /// if a global bound is safe to evaluate. - const HAS_RE_LATE_BOUND = 1 << 11; + const HAS_RE_LATE_BOUND = 1 << 11; const HAS_TY_PLACEHOLDER = 1 << 12; - const HAS_CT_INFER = 1 << 13; + const HAS_CT_INFER = 1 << 13; const HAS_CT_PLACEHOLDER = 1 << 14; const NEEDS_SUBST = TypeFlags::HAS_PARAMS.bits | @@ -479,7 +499,7 @@ #[allow(rustc::usage_of_ty_tykind)] pub struct TyS<'tcx> { - pub sty: TyKind<'tcx>, + pub kind: TyKind<'tcx>, pub flags: TypeFlags, /// This is a kind of confusing thing: it stores the smallest @@ -508,13 +528,13 @@ impl<'tcx> Ord for TyS<'tcx> { fn cmp(&self, other: &TyS<'tcx>) -> Ordering { - self.sty.cmp(&other.sty) + self.kind.cmp(&other.kind) } } impl<'tcx> PartialOrd for TyS<'tcx> { fn partial_cmp(&self, other: &TyS<'tcx>) -> Option<Ordering> { - Some(self.sty.cmp(&other.sty)) + Some(self.kind.cmp(&other.kind)) } } @@ -534,7 +554,7 @@ impl<'tcx> TyS<'tcx> { pub fn is_primitive_ty(&self) -> bool { - match self.sty { + match self.kind { Bool | Char | Int(_) | @@ -550,7 +570,7 @@ } pub fn is_suggestable(&self) -> bool { - match self.sty { + match self.kind { Opaque(..) | FnDef(..) | FnPtr(..) | @@ -564,24 +584,22 @@ } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::TyS<'tcx> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TyS { - ref sty, + ref kind, // The other fields just provide fast access to information that is - // also contained in `sty`, so no need to hash them. + // also contained in `kind`, so no need to hash them. flags: _, outer_exclusive_binder: _, } = *self; - sty.hash_stable(hcx, hasher); + kind.hash_stable(hcx, hasher); } } -#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "Ty")] +#[rustc_diagnostic_item = "Ty"] pub type Ty<'tcx> = &'tcx TyS<'tcx>; impl<'tcx> rustc_serialize::UseSpecializedEncodable for Ty<'tcx> {} @@ -590,7 +608,8 @@ pub type CanonicalTy<'tcx> = Canonical<'tcx, Ty<'tcx>>; extern { - /// A dummy type used to force `List` to by unsized without requiring fat pointers. + /// A dummy type used to force `List` to be unsized while not requiring references to it be wide + /// pointers. type OpaqueListContents; } @@ -689,6 +708,13 @@ type Target = [T]; #[inline(always)] fn deref(&self) -> &[T] { + self.as_ref() + } +} + +impl<T> AsRef<[T]> for List<T> { + #[inline(always)] + fn as_ref(&self) -> &[T] { unsafe { slice::from_raw_parts(self.data.as_ptr(), self.len) } @@ -733,7 +759,7 @@ pub closure_expr_id: LocalDefId, } -#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable, Copy, HashStable)] +#[derive(Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, HashStable)] pub enum BorrowKind { /// Data must be immutable and is aliasable. ImmBorrow, @@ -830,7 +856,7 @@ /// Does this early bound region have a name? Early bound regions normally /// always have names except when using anonymous lifetimes (`'_`). pub fn has_name(&self) -> bool { - self.name != kw::UnderscoreLifetime.as_interned_str() + self.name != kw::UnderscoreLifetime } } @@ -847,7 +873,7 @@ #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] pub struct GenericParamDef { - pub name: InternedString, + pub name: Symbol, pub def_id: DefId, pub index: u32, @@ -999,15 +1025,12 @@ } /// Bounds on generics. -#[derive(Clone, Default, Debug, HashStable)] +#[derive(Copy, Clone, Default, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct GenericPredicates<'tcx> { pub parent: Option<DefId>, - pub predicates: Vec<(Predicate<'tcx>, Span)>, + pub predicates: &'tcx [(Predicate<'tcx>, Span)], } -impl<'tcx> rustc_serialize::UseSpecializedEncodable for GenericPredicates<'tcx> {} -impl<'tcx> rustc_serialize::UseSpecializedDecodable for GenericPredicates<'tcx> {} - impl<'tcx> GenericPredicates<'tcx> { pub fn instantiate( &self, @@ -1100,7 +1123,7 @@ /// No direct syntax. May be thought of as `where T: FnFoo<...>` /// for some substitutions `...` and `T` being a closure type. /// Satisfied (or refuted) once we know the closure's kind. - ClosureKind(DefId, ClosureSubsts<'tcx>, ClosureKind), + ClosureKind(DefId, SubstsRef<'tcx>, ClosureKind), /// `T1 <: T2` Subtype(PolySubtypePredicate<'tcx>), @@ -1120,7 +1143,7 @@ /// For each struct with outlive bounds, maps to a vector of the /// predicate of its outlive bounds. If an item has no outlives /// bounds, it will have no entry. - pub predicates: FxHashMap<DefId, &'tcx [ty::Predicate<'tcx>]>, + pub predicates: FxHashMap<DefId, &'tcx [(ty::Predicate<'tcx>, Span)]>, } impl<'tcx> AsRef<Predicate<'tcx>> for Predicate<'tcx> { @@ -1447,7 +1470,7 @@ WalkTysIter::None } ty::Predicate::ClosureKind(_closure_def_id, closure_substs, _kind) => { - WalkTysIter::Types(closure_substs.substs.types()) + WalkTysIter::Types(closure_substs.types()) } ty::Predicate::ConstEvaluatable(_, substs) => { WalkTysIter::Types(substs.types()) @@ -1526,7 +1549,7 @@ } } -newtype_index! { +rustc_index::newtype_index! { /// "Universes" are used during type- and trait-checking in the /// presence of `for<..>` binders to control what sets of names are /// visible. Universes are arranged into a tree: the root universe @@ -1620,11 +1643,7 @@ where T: HashStable<StableHashingContext<'a>>, { - fn hash_stable<W: StableHasherResult>( - &self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W> - ) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.universe.hash_stable(hcx, hasher); self.name.hash_stable(hcx, hasher); } @@ -1761,9 +1780,7 @@ where T: HashStable<StableHashingContext<'a>>, { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ParamEnvAnd { ref param_env, ref value @@ -1997,9 +2014,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for AdtDef { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { thread_local! { static CACHE: RefCell<FxHashMap<usize, Fingerprint>> = Default::default(); } @@ -2310,7 +2325,7 @@ } #[inline] - pub fn predicates(&self, tcx: TyCtxt<'tcx>) -> &'tcx GenericPredicates<'tcx> { + pub fn predicates(&self, tcx: TyCtxt<'tcx>) -> GenericPredicates<'tcx> { tcx.predicates_of(self.did) } @@ -2365,7 +2380,7 @@ pub fn eval_explicit_discr(&self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option<Discr<'tcx>> { let param_env = tcx.param_env(expr_did); let repr_type = self.repr.discr_type(); - let substs = InternalSubsts::identity_for_item(tcx.global_tcx(), expr_did); + let substs = InternalSubsts::identity_for_item(tcx, expr_did); let instance = ty::Instance::new(expr_did, substs); let cid = GlobalId { instance, @@ -2374,7 +2389,7 @@ match tcx.const_eval(param_env.and(cid)) { Ok(val) => { // FIXME: Find the right type and use it instead of `val.ty` here - if let Some(b) = val.try_eval_bits(tcx.global_tcx(), param_env, val.ty) { + if let Some(b) = val.try_eval_bits(tcx, param_env, val.ty) { trace!("discriminants: {} ({:?})", b, repr_type); Some(Discr { val: b, @@ -2410,7 +2425,7 @@ tcx: TyCtxt<'tcx>, ) -> impl Iterator<Item = (VariantIdx, Discr<'tcx>)> + Captures<'tcx> { let repr_type = self.repr.discr_type(); - let initial = repr_type.initial_discriminant(tcx.global_tcx()); + let initial = repr_type.initial_discriminant(tcx); let mut prev_discr = None::<Discr<'tcx>>; self.variants.iter_enumerated().map(move |(i, v)| { let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); @@ -2444,7 +2459,7 @@ let (val, offset) = self.discriminant_def_for_variant(variant_index); let explicit_value = val .and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did)) - .unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx.global_tcx())); + .unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx)); explicit_value.checked_add(tcx, offset as u128).0 } @@ -2494,7 +2509,7 @@ } fn sized_constraint_for_ty(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Vec<Ty<'tcx>> { - let result = match ty.sty { + let result = match ty.kind { Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..) | FnPtr(_) | Array(..) | Closure(..) | Generator(..) | Never => { @@ -2550,7 +2565,7 @@ def_id: sized_trait, substs: tcx.mk_substs_trait(ty, &[]) }).to_predicate(); - let predicates = &tcx.predicates_of(self.did).predicates; + let predicates = tcx.predicates_of(self.did).predicates; if predicates.iter().any(|(p, _)| *p == sized_predicate) { vec![] } else { @@ -2911,7 +2926,26 @@ return Some(ImplOverlapKind::Permitted); } - let is_legit = if self.features().overlapping_marker_traits { + match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) { + (ImplPolarity::Reservation, _) | + (_, ImplPolarity::Reservation) => { + // `#[rustc_reservation_impl]` impls don't overlap with anything + debug!("impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (reservations)", + def_id1, def_id2); + return Some(ImplOverlapKind::Permitted); + } + (ImplPolarity::Positive, ImplPolarity::Negative) | + (ImplPolarity::Negative, ImplPolarity::Positive) => { + // `impl AutoTrait for Type` + `impl !AutoTrait for Type` + debug!("impls_are_allowed_to_overlap({:?}, {:?}) - None (differing polarities)", + def_id1, def_id2); + return None; + } + (ImplPolarity::Positive, ImplPolarity::Positive) | + (ImplPolarity::Negative, ImplPolarity::Negative) => {} + }; + + let is_marker_overlap = if self.features().overlapping_marker_traits { let trait1_is_empty = self.impl_trait_ref(def_id1) .map_or(false, |trait_ref| { self.associated_item_def_ids(trait_ref.def_id).is_empty() @@ -2920,22 +2954,19 @@ .map_or(false, |trait_ref| { self.associated_item_def_ids(trait_ref.def_id).is_empty() }); - self.impl_polarity(def_id1) == self.impl_polarity(def_id2) - && trait1_is_empty - && trait2_is_empty + trait1_is_empty && trait2_is_empty } else { let is_marker_impl = |def_id: DefId| -> bool { let trait_ref = self.impl_trait_ref(def_id); trait_ref.map_or(false, |tr| self.trait_def(tr.def_id).is_marker) }; - self.impl_polarity(def_id1) == self.impl_polarity(def_id2) - && is_marker_impl(def_id1) - && is_marker_impl(def_id2) + is_marker_impl(def_id1) && is_marker_impl(def_id2) }; - if is_legit { - debug!("impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted)", - def_id1, def_id2); + + if is_marker_overlap { + debug!("impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (marker overlap)", + def_id1, def_id2); Some(ImplOverlapKind::Permitted) } else { if let Some(self_ty1) = self.issue33140_self_ty(def_id1) { @@ -2995,7 +3026,7 @@ }), _ => def_key.disambiguated_data.data.get_opt_name().unwrap_or_else(|| { bug!("item_name: no name for {:?}", self.def_path(id)); - }).as_symbol(), + }), } } } @@ -3007,6 +3038,7 @@ self.optimized_mir(did) } ty::InstanceDef::VtableShim(..) | + ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::Intrinsic(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::Virtual(..) | @@ -3114,6 +3146,7 @@ } } +#[derive(Clone)] pub struct AssocItemsIterator<'tcx> { tcx: TyCtxt<'tcx>, def_ids: &'tcx [DefId], @@ -3135,7 +3168,7 @@ let parent_id = tcx.hir().get_parent_item(id); let parent_def_id = tcx.hir().local_def_id(parent_id); let parent_item = tcx.hir().expect_item(parent_id); - match parent_item.node { + match parent_item.kind { hir::ItemKind::Impl(.., ref impl_item_refs) => { if let Some(impl_item_ref) = impl_item_refs.iter().find(|i| i.id.hir_id == id) { let assoc_item = tcx.associated_item_from_impl_item_ref(parent_def_id, @@ -3160,7 +3193,7 @@ span_bug!(parent_item.span, "unexpected parent of trait or impl item or item not found: {:?}", - parent_item.node) + parent_item.kind) } #[derive(Clone, HashStable)] @@ -3192,7 +3225,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { let id = tcx.hir().as_local_hir_id(def_id).unwrap(); let item = tcx.hir().expect_item(id); - match item.node { + match item.kind { hir::ItemKind::Trait(.., ref trait_item_refs) => { tcx.arena.alloc_from_iter( trait_item_refs.iter() @@ -3233,7 +3266,7 @@ pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> { if let Some(hir_id) = tcx.hir().as_local_hir_id(def_id) { if let Node::Item(item) = tcx.hir().get(hir_id) { - if let hir::ItemKind::OpaqueTy(ref opaque_ty) = item.node { + if let hir::ItemKind::OpaqueTy(ref opaque_ty) = item.kind { return opaque_ty.impl_trait_fn; } } @@ -3317,7 +3350,7 @@ debug!("issue33140_self_ty({:?}), trait-ref={:?}", def_id, trait_ref); let is_marker_like = - tcx.impl_polarity(def_id) == hir::ImplPolarity::Positive && + tcx.impl_polarity(def_id) == ty::ImplPolarity::Positive && tcx.associated_item_def_ids(trait_ref.def_id).is_empty(); // Check whether these impls would be ok for a marker trait. @@ -3339,7 +3372,7 @@ } let self_ty = trait_ref.self_ty(); - let self_ty_matches = match self_ty.sty { + let self_ty_matches = match self_ty.kind { ty::Dynamic(ref data, ty::ReStatic) => data.principal().is_none(), _ => false }; @@ -3368,13 +3401,13 @@ fn_like.asyncness() } - pub fn provide(providers: &mut ty::query::Providers<'_>) { context::provide(providers); erase_regions::provide(providers); layout::provide(providers); util::provide(providers); constness::provide(providers); + crate::traits::query::dropck_outlives::provide(providers); *providers = ty::query::Providers { asyncness, associated_item, @@ -3403,11 +3436,11 @@ pub inherent_impls: DefIdMap<Vec<DefId>>, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub struct SymbolName { // FIXME: we don't rely on interning or equality here - better have // this be a `&'tcx str`. - pub name: InternedString + pub name: Symbol } impl_stable_hash_for!(struct self::SymbolName { @@ -3417,11 +3450,24 @@ impl SymbolName { pub fn new(name: &str) -> SymbolName { SymbolName { - name: InternedString::intern(name) + name: Symbol::intern(name) } } } +impl PartialOrd for SymbolName { + fn partial_cmp(&self, other: &SymbolName) -> Option<Ordering> { + self.name.as_str().partial_cmp(&other.name.as_str()) + } +} + +/// Ordering must use the chars to ensure reproducible builds. +impl Ord for SymbolName { + fn cmp(&self, other: &SymbolName) -> Ordering { + self.name.as_str().cmp(&other.name.as_str()) + } +} + impl fmt::Display for SymbolName { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.name, fmt)
diff --git a/src/librustc/ty/outlives.rs b/src/librustc/ty/outlives.rs index 7d1403d..80e77cd 100644 --- a/src/librustc/ty/outlives.rs +++ b/src/librustc/ty/outlives.rs
@@ -60,16 +60,16 @@ // with `collect()` because of the need to sometimes skip subtrees // in the `subtys` iterator (e.g., when encountering a // projection). - match ty.sty { + match ty.kind { ty::Closure(def_id, ref substs) => { - for upvar_ty in substs.upvar_tys(def_id, *self) { + for upvar_ty in substs.as_closure().upvar_tys(def_id, *self) { self.compute_components(upvar_ty, out); } } ty::Generator(def_id, ref substs, _) => { // Same as the closure case - for upvar_ty in substs.upvar_tys(def_id, *self) { + for upvar_ty in substs.as_generator().upvar_tys(def_id, *self) { self.compute_components(upvar_ty, out); }
diff --git a/src/librustc/ty/print/mod.rs b/src/librustc/ty/print/mod.rs index 50789bf..d216c81 100644 --- a/src/librustc/ty/print/mod.rs +++ b/src/librustc/ty/print/mod.rs
@@ -1,7 +1,7 @@ use crate::hir::map::{DefPathData, DisambiguatedDefPathData}; use crate::hir::def_id::{CrateNum, DefId}; use crate::ty::{self, DefIdTree, Ty, TyCtxt}; -use crate::ty::subst::{Kind, Subst}; +use crate::ty::subst::{GenericArg, Subst}; use rustc_data_structures::fx::FxHashSet; @@ -43,7 +43,7 @@ fn print_def_path( self, def_id: DefId, - substs: &'tcx [Kind<'tcx>], + substs: &'tcx [GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { self.default_print_def_path(def_id, substs) } @@ -51,7 +51,7 @@ fn print_impl_path( self, impl_def_id: DefId, - substs: &'tcx [Kind<'tcx>], + substs: &'tcx [GenericArg<'tcx>], self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, ) -> Result<Self::Path, Self::Error> { @@ -106,7 +106,7 @@ fn path_generic_args( self, print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, - args: &[Kind<'tcx>], + args: &[GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error>; // Defaults (should not be overriden): @@ -114,7 +114,7 @@ fn default_print_def_path( self, def_id: DefId, - substs: &'tcx [Kind<'tcx>], + substs: &'tcx [GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { debug!("default_print_def_path: def_id={:?}, substs={:?}", def_id, substs); let key = self.tcx().def_key(def_id); @@ -189,8 +189,8 @@ fn generic_args_to_print( &self, generics: &'tcx ty::Generics, - substs: &'tcx [Kind<'tcx>], - ) -> &'tcx [Kind<'tcx>] { + substs: &'tcx [GenericArg<'tcx>], + ) -> &'tcx [GenericArg<'tcx>] { let mut own_params = generics.parent_count..generics.count(); // Don't print args for `Self` parameters (of traits). @@ -203,7 +203,7 @@ match param.kind { ty::GenericParamDefKind::Lifetime => false, ty::GenericParamDefKind::Type { has_default, .. } => { - has_default && substs[param.index as usize] == Kind::from( + has_default && substs[param.index as usize] == GenericArg::from( self.tcx().type_of(param.def_id).subst(self.tcx(), substs) ) } @@ -217,7 +217,7 @@ fn default_print_impl_path( self, impl_def_id: DefId, - _substs: &'tcx [Kind<'tcx>], + _substs: &'tcx [GenericArg<'tcx>], self_ty: Ty<'tcx>, impl_trait_ref: Option<ty::TraitRef<'tcx>>, ) -> Result<Self::Path, Self::Error> { @@ -266,7 +266,7 @@ /// type. It's just a heuristic so it makes some questionable /// decisions and we may want to adjust it later. pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> { - match ty.sty { + match ty.kind { ty::Adt(adt_def, _) => Some(adt_def.did), ty::Dynamic(data, ..) => data.principal_def_id(),
diff --git a/src/librustc/ty/print/obsolete.rs b/src/librustc/ty/print/obsolete.rs index b68e6a7..e72916d 100644 --- a/src/librustc/ty/print/obsolete.rs +++ b/src/librustc/ty/print/obsolete.rs
@@ -8,7 +8,7 @@ use rustc::hir::def_id::DefId; use rustc::mir::interpret::ConstValue; use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, ClosureSubsts, Const, GeneratorSubsts, Instance, Ty, TyCtxt}; +use rustc::ty::{self, Const, Instance, Ty, TyCtxt}; use rustc::{bug, hir}; use std::fmt::Write; use std::iter; @@ -34,7 +34,7 @@ // When being used for codegen purposes, `debug` should be set to `false` // in order to catch unexpected types that should never end up in a type name. pub fn push_type_name(&self, t: Ty<'tcx>, output: &mut String, debug: bool) { - match t.sty { + match t.kind { ty::Bool => output.push_str("bool"), ty::Char => output.push_str("char"), ty::Str => output.push_str("str"), @@ -154,8 +154,8 @@ self.push_type_name(sig.output(), output, debug); } } - ty::Generator(def_id, GeneratorSubsts { ref substs }, _) - | ty::Closure(def_id, ClosureSubsts { ref substs }) => { + ty::Generator(def_id, substs, _) + | ty::Closure(def_id, substs) => { self.push_def_path(def_id, output); let generics = self.tcx.generics_of(self.tcx.closure_base_def_id(def_id)); let substs = substs.truncate_to(self.tcx, generics); @@ -218,9 +218,9 @@ // foo::bar::ItemName:: for part in self.tcx.def_path(def_id).data { if self.omit_disambiguators { - write!(output, "{}::", part.data.as_interned_str()).unwrap(); + write!(output, "{}::", part.data.as_symbol()).unwrap(); } else { - write!(output, "{}[{}]::", part.data.as_interned_str(), part.disambiguator) + write!(output, "{}[{}]::", part.data.as_symbol(), part.disambiguator) .unwrap(); } }
diff --git a/src/librustc/ty/print/pretty.rs b/src/librustc/ty/print/pretty.rs index d995801..8a98a5d 100644 --- a/src/librustc/ty/print/pretty.rs +++ b/src/librustc/ty/print/pretty.rs
@@ -5,7 +5,7 @@ use crate::middle::cstore::{ExternCrate, ExternCrateSource}; use crate::middle::region; use crate::ty::{self, DefIdTree, ParamConst, Ty, TyCtxt, TypeFoldable}; -use crate::ty::subst::{Kind, Subst, UnpackedKind}; +use crate::ty::subst::{GenericArg, Subst, GenericArgKind}; use crate::ty::layout::{Integer, IntegerExt, Size}; use crate::mir::interpret::{ConstValue, sign_extend, Scalar, truncate}; @@ -14,7 +14,7 @@ use rustc_target::spec::abi::Abi; use syntax::ast; use syntax::attr::{SignedInt, UnsignedInt}; -use syntax::symbol::{kw, InternedString}; +use syntax::symbol::{kw, Symbol}; use std::cell::Cell; use std::fmt::{self, Write as _}; @@ -183,7 +183,7 @@ fn print_value_path( self, def_id: DefId, - substs: &'tcx [Kind<'tcx>], + substs: &'tcx [GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { self.print_def_path(def_id, substs) } @@ -278,7 +278,7 @@ match self.tcx().extern_crate(def_id) { Some(&ExternCrate { src: ExternCrateSource::Extern(def_id), - direct: true, + dependency_of: LOCAL_CRATE, span, .. }) => { @@ -384,7 +384,7 @@ let reexport = self.tcx().item_children(visible_parent) .iter() .find(|child| child.res.def_id() == def_id) - .map(|child| child.ident.as_interned_str()); + .map(|child| child.ident.name); if let Some(reexport) = reexport { *name = reexport; } @@ -392,7 +392,7 @@ // Re-exported `extern crate` (#43189). DefPathData::CrateRoot => { data = DefPathData::TypeNs( - self.tcx().original_crate_name(def_id.krate).as_interned_str(), + self.tcx().original_crate_name(def_id.krate), ); } _ => {} @@ -414,7 +414,7 @@ // Inherent impls. Try to print `Foo::bar` for an inherent // impl on `Foo`, but fallback to `<Foo>::bar` if self-type is // anything other than a simple path. - match self_ty.sty { + match self_ty.kind { ty::Adt(..) | ty::Foreign(_) | ty::Bool | ty::Char | ty::Str | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { @@ -463,7 +463,7 @@ ) -> Result<Self::Type, Self::Error> { define_scoped_cx!(self); - match ty.sty { + match ty.kind { ty::Bool => p!(write("bool")), ty::Char => p!(write("char")), ty::Int(t) => p!(write("{}", t.ty_to_string())), @@ -605,8 +605,8 @@ } ty::Str => p!(write("str")), ty::Generator(did, substs, movability) => { - let upvar_tys = substs.upvar_tys(did, self.tcx()); - let witness = substs.witness(did, self.tcx()); + let upvar_tys = substs.as_generator().upvar_tys(did, self.tcx()); + let witness = substs.as_generator().witness(did, self.tcx()); if movability == hir::GeneratorMovability::Movable { p!(write("[generator")); } else { @@ -649,7 +649,7 @@ p!(in_binder(&types)); } ty::Closure(did, substs) => { - let upvar_tys = substs.upvar_tys(did, self.tcx()); + let upvar_tys = substs.as_closure().upvar_tys(did, self.tcx()); p!(write("[closure")); // FIXME(eddyb) should use `def_span`. @@ -689,8 +689,8 @@ if self.tcx().sess.verbose() { p!(write( " closure_kind_ty={:?} closure_sig_ty={:?}", - substs.closure_kind_ty(did, self.tcx()), - substs.closure_sig_ty(did, self.tcx()) + substs.as_closure().kind_ty(did, self.tcx()), + substs.as_closure().sig_ty(did, self.tcx()) )); } @@ -698,7 +698,9 @@ }, ty::Array(ty, sz) => { p!(write("["), print(ty), write("; ")); - if let ConstValue::Unevaluated(..) = sz.val { + if self.tcx().sess.verbose() { + p!(write("{:?}", sz)); + } else if let ConstValue::Unevaluated(..) = sz.val { // do not try to evalute unevaluated constants. If we are const evaluating an // array length anon const, rustc will (with debug assertions) print the // constant's path. Which will end up here again. @@ -739,7 +741,7 @@ // Special-case `Fn(...) -> ...` and resugar it. let fn_trait_kind = self.tcx().lang_items().fn_trait_kind(principal.def_id); if !self.tcx().sess.verbose() && fn_trait_kind.is_some() { - if let ty::Tuple(ref args) = principal.substs.type_at(0).sty { + if let ty::Tuple(ref args) = principal.substs.type_at(0).kind { let mut projections = predicates.projection_bounds(); if let (Some(proj), None) = (projections.next(), projections.next()) { let tys: Vec<_> = args.iter().map(|k| k.expect_ty()).collect(); @@ -764,13 +766,13 @@ // Don't print `'_` if there's no unerased regions. let print_regions = args.iter().any(|arg| { match arg.unpack() { - UnpackedKind::Lifetime(r) => *r != ty::ReErased, + GenericArgKind::Lifetime(r) => *r != ty::ReErased, _ => false, } }); let mut args = args.iter().cloned().filter(|arg| { match arg.unpack() { - UnpackedKind::Lifetime(_) => print_regions, + GenericArgKind::Lifetime(_) => print_regions, _ => true, } }); @@ -855,126 +857,127 @@ ) -> Result<Self::Const, Self::Error> { define_scoped_cx!(self); - let u8 = self.tcx().types.u8; - if let ty::FnDef(did, substs) = ct.ty.sty { - p!(print_value_path(did, substs)); + if self.tcx().sess.verbose() { + p!(write("Const({:?}: {:?})", ct.val, ct.ty)); return Ok(self); } - if let ConstValue::Unevaluated(did, substs) = ct.val { - match self.tcx().def_kind(did) { - | Some(DefKind::Static) - | Some(DefKind::Const) - | Some(DefKind::AssocConst) => p!(print_value_path(did, substs)), - _ => if did.is_local() { - let span = self.tcx().def_span(did); - if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) { - p!(write("{}", snip)) + + let u8 = self.tcx().types.u8; + + match (ct.val, &ct.ty.kind) { + (_, ty::FnDef(did, substs)) => p!(print_value_path(*did, substs)), + (ConstValue::Unevaluated(did, substs), _) => { + match self.tcx().def_kind(did) { + | Some(DefKind::Static) + | Some(DefKind::Const) + | Some(DefKind::AssocConst) => p!(print_value_path(did, substs)), + _ => if did.is_local() { + let span = self.tcx().def_span(did); + if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) { + p!(write("{}", snip)) + } else { + p!(write("_: "), print(ct.ty)) + } } else { p!(write("_: "), print(ct.ty)) + }, + } + }, + (ConstValue::Infer(..), _) => p!(write("_: "), print(ct.ty)), + (ConstValue::Param(ParamConst { name, .. }), _) => p!(write("{}", name)), + (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Bool) => + p!(write("{}", if data == 0 { "false" } else { "true" })), + (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Float(ast::FloatTy::F32)) => + p!(write("{}f32", Single::from_bits(data))), + (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Float(ast::FloatTy::F64)) => + p!(write("{}f64", Double::from_bits(data))), + (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Uint(ui)) => { + let bit_size = Integer::from_attr(&self.tcx(), UnsignedInt(*ui)).size(); + let max = truncate(u128::max_value(), bit_size); + + if data == max { + p!(write("std::{}::MAX", ui)) + } else { + p!(write("{}{}", data, ui)) + }; + }, + (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Int(i)) => { + let bit_size = Integer::from_attr(&self.tcx(), SignedInt(*i)) + .size().bits() as u128; + let min = 1u128 << (bit_size - 1); + let max = min - 1; + + let ty = self.tcx().lift(&ct.ty).unwrap(); + let size = self.tcx().layout_of(ty::ParamEnv::empty().and(ty)) + .unwrap() + .size; + match data { + d if d == min => p!(write("std::{}::MIN", i)), + d if d == max => p!(write("std::{}::MAX", i)), + _ => p!(write("{}{}", sign_extend(data, size) as i128, i)) + } + }, + (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Char) => + p!(write("{:?}", ::std::char::from_u32(data as u32).unwrap())), + (ConstValue::Scalar(_), ty::RawPtr(_)) => p!(write("{{pointer}}")), + (ConstValue::Scalar(Scalar::Ptr(ptr)), ty::FnPtr(_)) => { + let instance = { + let alloc_map = self.tcx().alloc_map.lock(); + alloc_map.unwrap_fn(ptr.alloc_id) + }; + p!(print_value_path(instance.def_id(), instance.substs)); + }, + _ => { + let printed = if let ty::Ref(_, ref_ty, _) = ct.ty.kind { + let byte_str = match (ct.val, &ref_ty.kind) { + (ConstValue::Scalar(Scalar::Ptr(ptr)), ty::Array(t, n)) if *t == u8 => { + let n = n.eval_usize(self.tcx(), ty::ParamEnv::empty()); + Some(self.tcx() + .alloc_map.lock() + .unwrap_memory(ptr.alloc_id) + .get_bytes(&self.tcx(), ptr, Size::from_bytes(n)).unwrap()) + }, + (ConstValue::Slice { data, start, end }, ty::Slice(t)) if *t == u8 => { + // The `inspect` here is okay since we checked the bounds, and there are + // no relocations (we have an active slice reference here). We don't use + // this result to affect interpreter execution. + Some(data.inspect_with_undef_and_ptr_outside_interpreter(start..end)) + }, + _ => None, + }; + + if let Some(byte_str) = byte_str { + p!(write("b\"")); + for &c in byte_str { + for e in std::ascii::escape_default(c) { + self.write_char(e as char)?; + } + } + p!(write("\"")); + true + } else if let (ConstValue::Slice { data, start, end }, ty::Str) = + (ct.val, &ref_ty.kind) + { + // The `inspect` here is okay since we checked the bounds, and there are no + // relocations (we have an active `str` reference here). We don't use this + // result to affect interpreter execution. + let slice = data.inspect_with_undef_and_ptr_outside_interpreter(start..end); + let s = ::std::str::from_utf8(slice) + .expect("non utf8 str from miri"); + p!(write("{:?}", s)); + true + } else { + false } } else { - p!(write("_: "), print(ct.ty)) - }, - } - return Ok(self); - } - if let ConstValue::Infer(..) = ct.val { - p!(write("_: "), print(ct.ty)); - return Ok(self); - } - if let ConstValue::Param(ParamConst { name, .. }) = ct.val { - p!(write("{}", name)); - return Ok(self); - } - if let ConstValue::Scalar(Scalar::Raw { data, .. }) = ct.val { - match ct.ty.sty { - ty::Bool => { - p!(write("{}", if data == 0 { "false" } else { "true" })); - return Ok(self); - }, - ty::Float(ast::FloatTy::F32) => { - p!(write("{}f32", Single::from_bits(data))); - return Ok(self); - }, - ty::Float(ast::FloatTy::F64) => { - p!(write("{}f64", Double::from_bits(data))); - return Ok(self); - }, - ty::Uint(ui) => { - let bit_size = Integer::from_attr(&self.tcx(), UnsignedInt(ui)).size(); - let max = truncate(u128::max_value(), bit_size); - - if data == max { - p!(write("std::{}::MAX", ui)) - } else { - p!(write("{}{}", data, ui)) - }; - return Ok(self); - }, - ty::Int(i) =>{ - let bit_size = Integer::from_attr(&self.tcx(), SignedInt(i)) - .size().bits() as u128; - let min = 1u128 << (bit_size - 1); - let max = min - 1; - - let ty = self.tcx().lift_to_global(&ct.ty).unwrap(); - let size = self.tcx().layout_of(ty::ParamEnv::empty().and(ty)) - .unwrap() - .size; - match data { - d if d == min => p!(write("std::{}::MIN", i)), - d if d == max => p!(write("std::{}::MAX", i)), - _ => p!(write("{}{}", sign_extend(data, size) as i128, i)) - } - return Ok(self); - }, - ty::Char => { - p!(write("{:?}", ::std::char::from_u32(data as u32).unwrap())); - return Ok(self); + false + }; + if !printed { + // fallback + p!(write("{:?} : ", ct.val), print(ct.ty)) } - _ => {}, } - } - if let ty::Ref(_, ref_ty, _) = ct.ty.sty { - let byte_str = match (ct.val, &ref_ty.sty) { - (ConstValue::Scalar(Scalar::Ptr(ptr)), ty::Array(t, n)) if *t == u8 => { - let n = n.eval_usize(self.tcx(), ty::ParamEnv::empty()); - Some(self.tcx() - .alloc_map.lock() - .unwrap_memory(ptr.alloc_id) - .get_bytes(&self.tcx(), ptr, Size::from_bytes(n)).unwrap()) - }, - (ConstValue::Slice { data, start, end }, ty::Slice(t)) if *t == u8 => { - // The `inspect` here is okay since we checked the bounds, and there are no - // relocations (we have an active slice reference here). We don't use this - // result to affect interpreter execution. - Some(data.inspect_with_undef_and_ptr_outside_interpreter(start..end)) - }, - (ConstValue::Slice { data, start, end }, ty::Str) => { - // The `inspect` here is okay since we checked the bounds, and there are no - // relocations (we have an active `str` reference here). We don't use this - // result to affect interpreter execution. - let slice = data.inspect_with_undef_and_ptr_outside_interpreter(start..end); - let s = ::std::str::from_utf8(slice) - .expect("non utf8 str from miri"); - p!(write("{:?}", s)); - return Ok(self); - }, - _ => None, - }; - if let Some(byte_str) = byte_str { - p!(write("b\"")); - for &c in byte_str { - for e in std::ascii::escape_default(c) { - self.write_char(e as char)?; - } - } - p!(write("\"")); - return Ok(self); - } - } - p!(write("{:?} : ", ct.val), print(ct.ty)); - + }; Ok(self) } } @@ -989,7 +992,7 @@ empty_path: bool, in_value: bool, - used_region_names: FxHashSet<InternedString>, + used_region_names: FxHashSet<Symbol>, region_index: usize, binder_depth: usize, @@ -1081,7 +1084,7 @@ fn print_def_path( mut self, def_id: DefId, - substs: &'tcx [Kind<'tcx>], + substs: &'tcx [GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { define_scoped_cx!(self); @@ -1219,7 +1222,7 @@ // FIXME(eddyb) `name` should never be empty, but it // currently is for `extern { ... }` "foreign modules". - let name = disambiguated_data.data.as_interned_str().as_str(); + let name = disambiguated_data.data.as_symbol().as_str(); if !name.is_empty() { if !self.empty_path { write!(self, "::")?; @@ -1245,20 +1248,20 @@ fn path_generic_args( mut self, print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, - args: &[Kind<'tcx>], + args: &[GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { self = print_prefix(self)?; // Don't print `'_` if there's no unerased regions. let print_regions = args.iter().any(|arg| { match arg.unpack() { - UnpackedKind::Lifetime(r) => *r != ty::ReErased, + GenericArgKind::Lifetime(r) => *r != ty::ReErased, _ => false, } }); let args = args.iter().cloned().filter(|arg| { match arg.unpack() { - UnpackedKind::Lifetime(_) => print_regions, + GenericArgKind::Lifetime(_) => print_regions, _ => true, } }); @@ -1282,7 +1285,7 @@ fn print_value_path( mut self, def_id: DefId, - substs: &'tcx [Kind<'tcx>], + substs: &'tcx [GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { let was_in_value = std::mem::replace(&mut self.in_value, true); self = self.print_def_path(def_id, substs)?; @@ -1329,16 +1332,16 @@ match *region { ty::ReEarlyBound(ref data) => { - data.name.as_symbol() != kw::Invalid && - data.name.as_symbol() != kw::UnderscoreLifetime + data.name != kw::Invalid && + data.name != kw::UnderscoreLifetime } ty::ReLateBound(_, br) | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { if let ty::BrNamed(_, name) = br { - if name.as_symbol() != kw::Invalid && - name.as_symbol() != kw::UnderscoreLifetime { + if name != kw::Invalid && + name != kw::UnderscoreLifetime { return true; } } @@ -1394,7 +1397,7 @@ // `explain_region()` or `note_and_explain_region()`. match *region { ty::ReEarlyBound(ref data) => { - if data.name.as_symbol() != kw::Invalid { + if data.name != kw::Invalid { p!(write("{}", data.name)); return Ok(self); } @@ -1403,8 +1406,8 @@ ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { if let ty::BrNamed(_, name) = br { - if name.as_symbol() != kw::Invalid && - name.as_symbol() != kw::UnderscoreLifetime { + if name != kw::Invalid && + name != kw::UnderscoreLifetime { p!(write("{}", name)); return Ok(self); } @@ -1471,16 +1474,16 @@ where T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, { - fn name_by_region_index(index: usize) -> InternedString { + fn name_by_region_index(index: usize) -> Symbol { match index { - 0 => InternedString::intern("'r"), - 1 => InternedString::intern("'s"), - i => InternedString::intern(&format!("'t{}", i-2)), + 0 => Symbol::intern("'r"), + 1 => Symbol::intern("'s"), + i => Symbol::intern(&format!("'t{}", i-2)), } } // Replace any anonymous late-bound regions with named - // variants, using gensym'd identifiers, so that we can + // variants, using new unique identifiers, so that we can // clearly differentiate between named and unnamed regions in // the output. We'll probably want to tweak this over time to // decide just how much information to give. @@ -1538,7 +1541,7 @@ where T: TypeFoldable<'tcx> { - struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet<InternedString>); + struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet<Symbol>); impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_> { fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { match *r { @@ -1778,11 +1781,11 @@ } } - Kind<'tcx> { + GenericArg<'tcx> { match self.unpack() { - UnpackedKind::Lifetime(lt) => p!(print(lt)), - UnpackedKind::Type(ty) => p!(print(ty)), - UnpackedKind::Const(ct) => p!(print(ct)), + GenericArgKind::Lifetime(lt) => p!(print(lt)), + GenericArgKind::Type(ty) => p!(print(ty)), + GenericArgKind::Const(ct) => p!(print(ct)), } } }
diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs index 91082c5..c1c6a65 100644 --- a/src/librustc/ty/query/config.rs +++ b/src/librustc/ty/query/config.rs
@@ -73,6 +73,17 @@ format!("processing {:?} with query `{}`", def_id, name).into() } } + + default fn cache_on_disk(_: TyCtxt<'tcx>, _: Self::Key, _: Option<&Self::Value>) -> bool { + false + } + + default fn try_load_from_disk( + _: TyCtxt<'tcx>, + _: SerializedDepNodeIndex, + ) -> Option<Self::Value> { + bug!("QueryDescription::load_from_disk() called for an unsupported query.") + } } impl<'tcx> QueryDescription<'tcx> for queries::analysis<'tcx> {
diff --git a/src/librustc/ty/query/job.rs b/src/librustc/ty/query/job.rs index a25560f..391ea76 100644 --- a/src/librustc/ty/query/job.rs +++ b/src/librustc/ty/query/job.rs
@@ -334,13 +334,13 @@ let mut hcx = tcx.create_stable_hashing_context(); queries.iter().min_by_key(|v| { let (span, query) = f(v); - let mut stable_hasher = StableHasher::<u64>::new(); + let mut stable_hasher = StableHasher::new(); query.info.query.hash_stable(&mut hcx, &mut stable_hasher); // Prefer entry points which have valid spans for nicer error messages // We add an integer to the tuple ensuring that entry points // with valid spans are picked first let span_cmp = if span == DUMMY_SP { 1 } else { 0 }; - (span_cmp, stable_hasher.finish()) + (span_cmp, stable_hasher.finish::<u64>()) }).unwrap() }
diff --git a/src/librustc/ty/query/keys.rs b/src/librustc/ty/query/keys.rs index 30a3e53..a9e0a5d 100644 --- a/src/librustc/ty/query/keys.rs +++ b/src/librustc/ty/query/keys.rs
@@ -8,14 +8,12 @@ use crate::ty::fast_reject::SimplifiedType; use crate::mir; -use std::fmt::Debug; -use std::hash::Hash; use syntax_pos::{Span, DUMMY_SP}; -use syntax_pos::symbol::InternedString; +use syntax_pos::symbol::Symbol; /// The `Key` trait controls what types can legally be used as the key /// for a query. -pub(super) trait Key: Clone + Hash + Eq + Debug { +pub(super) trait Key { /// Given an instance of this key, what crate is it referring to? /// This is used to find the provider. fn query_crate(&self) -> CrateNum; @@ -190,7 +188,7 @@ } } -impl Key for InternedString { +impl Key for Symbol { fn query_crate(&self) -> CrateNum { LOCAL_CRATE } @@ -201,10 +199,17 @@ /// Canonical query goals correspond to abstract trait operations that /// are not tied to any crate in particular. -impl<'tcx, T> Key for Canonical<'tcx, T> -where - T: Debug + Hash + Clone + Eq, -{ +impl<'tcx, T> Key for Canonical<'tcx, T> { + fn query_crate(&self) -> CrateNum { + LOCAL_CRATE + } + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + +impl Key for (Symbol, u32, u32) { fn query_crate(&self) -> CrateNum { LOCAL_CRATE }
diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index fb2ad2a..9b15ad5 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs
@@ -4,7 +4,6 @@ use crate::hir::{self, TraitCandidate, ItemLocalId, CodegenFnAttrs}; use crate::infer::canonical::{self, Canonical}; use crate::lint; -use crate::middle::borrowck::{BorrowCheckResult, SignalledError}; use crate::middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary, ForeignModule}; use crate::middle::cstore::{NativeLibraryKind, DepKind, CrateSource}; use crate::middle::privacy::AccessLevels; @@ -38,13 +37,13 @@ use crate::ty::steal::Steal; use crate::ty::util::NeedsDrop; use crate::ty::subst::SubstsRef; -use crate::util::nodemap::{DefIdSet, DefIdMap, ItemLocalSet}; -use crate::util::common::{ErrorReported}; +use crate::util::nodemap::{DefIdSet, DefIdMap}; +use crate::util::common::ErrorReported; use crate::util::profiling::ProfileCategory::*; use rustc_data_structures::svh::Svh; -use rustc_data_structures::bit_set::BitSet; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; use rustc_data_structures::fx::{FxIndexMap, FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::StableVec; use rustc_data_structures::sync::Lrc; @@ -56,7 +55,6 @@ use std::sync::Arc; use std::any::type_name; use syntax_pos::{Span, DUMMY_SP}; -use syntax_pos::symbol::InternedString; use syntax::attr; use syntax::ast; use syntax::feature_gate;
diff --git a/src/librustc/ty/query/on_disk_cache.rs b/src/librustc/ty/query/on_disk_cache.rs index c20e758..21a7cf0 100644 --- a/src/librustc/ty/query/on_disk_cache.rs +++ b/src/librustc/ty/query/on_disk_cache.rs
@@ -15,7 +15,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::sync::{Lrc, Lock, HashMapExt, Once}; -use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use rustc_index::vec::{IndexVec, Idx}; use rustc_serialize::{ Decodable, Decoder, Encodable, Encoder, SpecializedDecoder, SpecializedEncoder, UseSpecializedDecodable, UseSpecializedEncodable, opaque, @@ -882,15 +882,16 @@ } } -impl<'a, 'tcx, E> SpecializedEncoder<ty::GenericPredicates<'tcx>> for CacheEncoder<'a, 'tcx, E> +impl<'a, 'tcx, E> SpecializedEncoder<&'tcx [(ty::Predicate<'tcx>, Span)]> + for CacheEncoder<'a, 'tcx, E> where E: 'a + TyEncoder, { #[inline] fn specialized_encode(&mut self, - predicates: &ty::GenericPredicates<'tcx>) + predicates: &&'tcx [(ty::Predicate<'tcx>, Span)]) -> Result<(), Self::Error> { - ty_codec::encode_predicates(self, predicates, + ty_codec::encode_spanned_predicates(self, predicates, |encoder| &mut encoder.predicate_shorthands) } } @@ -1075,7 +1076,7 @@ let desc = &format!("encode_query_results for {}", ::std::any::type_name::<Q>()); - time_ext(tcx.sess.time_extended(), Some(tcx.sess), desc, || { + time_ext(tcx.sess.time_extended(), desc, || { let shards = Q::query_cache(tcx).lock_shards(); assert!(shards.iter().all(|shard| shard.active.is_empty())); for (key, entry) in shards.iter().flat_map(|shard| shard.results.iter()) {
diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index a1828bb..538154b 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs
@@ -9,12 +9,11 @@ use crate::ty::query::config::{QueryConfig, QueryDescription}; use crate::ty::query::job::{QueryJob, QueryResult, QueryInfo}; -use crate::util::common::{profq_msg, ProfileQueriesMsg, QueryMsg}; - use errors::DiagnosticBuilder; use errors::Level; use errors::Diagnostic; use errors::FatalError; +use errors::Handler; use rustc_data_structures::fx::{FxHashMap}; use rustc_data_structures::sync::{Lrc, Lock}; use rustc_data_structures::sharded::Sharded; @@ -61,33 +60,6 @@ } } -// If enabled, sends a message to the profile-queries thread. -macro_rules! profq_msg { - ($tcx:expr, $msg:expr) => { - if cfg!(debug_assertions) { - if $tcx.sess.profile_queries() { - profq_msg($tcx.sess, $msg) - } - } - } -} - -// If enabled, formats a key using its debug string, which can be -// expensive to compute (in terms of time). -macro_rules! profq_query_msg { - ($query:expr, $tcx:expr, $key:expr) => {{ - let msg = if cfg!(debug_assertions) { - if $tcx.sess.profile_queries_and_keys() { - Some(format!("{:?}", $key)) - } else { None } - } else { None }; - QueryMsg { - query: $query, - msg, - } - }} -} - /// A type representing the responsibility to execute the job in the `job` field. /// This will poison the relevant query if dropped. pub(super) struct JobOwner<'a, 'tcx, Q: QueryDescription<'tcx>> { @@ -110,8 +82,7 @@ loop { let mut lock = cache.get_shard_by_value(key).lock(); if let Some(value) = lock.results.get(key) { - profq_msg!(tcx, ProfileQueriesMsg::CacheHit); - tcx.sess.profiler(|p| p.record_query_hit(Q::NAME)); + tcx.prof.query_cache_hit(Q::NAME); let result = (value.value.clone(), value.index); #[cfg(debug_assertions)] { @@ -119,6 +90,10 @@ } return TryGetJob::JobCompleted(result); } + + #[cfg(parallel_compiler)] + let query_blocked_prof_timer; + let job = match lock.active.entry((*key).clone()) { Entry::Occupied(entry) => { match *entry.get() { @@ -127,7 +102,9 @@ // in another thread has completed. Record how long we wait in the // self-profiler. #[cfg(parallel_compiler)] - tcx.sess.profiler(|p| p.query_blocked_start(Q::NAME)); + { + query_blocked_prof_timer = tcx.prof.query_blocked(Q::NAME); + } job.clone() }, @@ -169,7 +146,11 @@ #[cfg(parallel_compiler)] { let result = job.r#await(tcx, span); - tcx.sess.profiler(|p| p.query_blocked_end(Q::NAME)); + + // This `drop()` is not strictly necessary as the binding + // would go out of scope anyway. But it's good to have an + // explicit marker of how far the measurement goes. + drop(query_blocked_prof_timer); if let Err(cycle) = result { return TryGetJob::Cycle(Q::handle_cycle_error(tcx, cycle)); @@ -265,7 +246,7 @@ tls::with_related_context(self, move |current_icx| { // Update the `ImplicitCtxt` to point to our new query job. let new_icx = tls::ImplicitCtxt { - tcx: self.global_tcx(), + tcx: self, query: Some(job), diagnostics, layout_depth: current_icx.layout_depth, @@ -274,7 +255,7 @@ // Use the `ImplicitCtxt` while we execute the query. tls::enter_context(&new_icx, |_| { - compute(self.global_tcx()) + compute(self) }) }) } @@ -321,9 +302,12 @@ }) } - pub fn try_print_query_stack() { + pub fn try_print_query_stack(handler: &Handler) { eprintln!("query stack during panic:"); + // Be careful reyling on global state here: this code is called from + // a panic hook, which means that the global `Handler` may be in a weird + // state if it was responsible for triggering the panic. tls::with_context_opt(|icx| { if let Some(icx) = icx { let mut current_query = icx.query.clone(); @@ -336,7 +320,7 @@ query.info.query.name(), query.info.query.describe(icx.tcx))); diag.span = icx.tcx.sess.source_map().def_span(query.info.span).into(); - icx.tcx.sess.diagnostic().force_print_diagnostic(diag); + handler.force_print_diagnostic(diag); current_query = query.parent.clone(); i += 1; @@ -354,13 +338,6 @@ key, span); - profq_msg!(self, - ProfileQueriesMsg::QueryBegin( - span.data(), - profq_query_msg!(Q::NAME.as_str(), self, key), - ) - ); - let job = match JobOwner::try_get(self, span, &key) { TryGetJob::NotYetStarted(job) => job, TryGetJob::Cycle(result) => return result, @@ -378,19 +355,18 @@ } if Q::ANON { - profq_msg!(self, ProfileQueriesMsg::ProviderBegin); - self.sess.profiler(|p| p.start_query(Q::NAME)); + + let prof_timer = self.prof.query_provider(Q::NAME); let ((result, dep_node_index), diagnostics) = with_diagnostics(|diagnostics| { self.start_query(job.job.clone(), diagnostics, |tcx| { tcx.dep_graph.with_anon_task(Q::dep_kind(), || { - Q::compute(tcx.global_tcx(), key) + Q::compute(tcx, key) }) }) }); - self.sess.profiler(|p| p.end_query(Q::NAME)); - profq_msg!(self, ProfileQueriesMsg::ProviderEnd); + drop(prof_timer); self.dep_graph.read_index(dep_node_index); @@ -445,11 +421,10 @@ debug_assert!(self.dep_graph.is_green(dep_node)); // First we try to load the result from the on-disk cache. - let result = if Q::cache_on_disk(self.global_tcx(), key.clone(), None) && + let result = if Q::cache_on_disk(self, key.clone(), None) && self.sess.opts.debugging_opts.incremental_queries { - self.sess.profiler(|p| p.incremental_load_result_start(Q::NAME)); - let result = Q::try_load_from_disk(self.global_tcx(), prev_dep_node_index); - self.sess.profiler(|p| p.incremental_load_result_end(Q::NAME)); + let _prof_timer = self.prof.incr_cache_loading(Q::NAME); + let result = Q::try_load_from_disk(self, prev_dep_node_index); // We always expect to find a cached result for things that // can be forced from `DepNode`. @@ -464,22 +439,17 @@ }; let result = if let Some(result) = result { - profq_msg!(self, ProfileQueriesMsg::CacheHit); - self.sess.profiler(|p| p.record_query_hit(Q::NAME)); - result } else { // We could not load a result from the on-disk cache, so // recompute. - - self.sess.profiler(|p| p.start_query(Q::NAME)); + let _prof_timer = self.prof.query_provider(Q::NAME); // The dep-graph for this computation is already in-place. let result = self.dep_graph.with_ignore(|| { Q::compute(self, key) }); - self.sess.profiler(|p| p.end_query(Q::NAME)); result }; @@ -489,10 +459,6 @@ self.incremental_verify_ich::<Q>(&result, dep_node, dep_node_index); } - if unlikely!(self.sess.opts.debugging_opts.query_dep_graph) { - self.dep_graph.mark_loaded_from_cache(dep_node_index, true); - } - result } @@ -546,8 +512,7 @@ - dep-node: {:?}", key, dep_node); - profq_msg!(self, ProfileQueriesMsg::ProviderBegin); - self.sess.profiler(|p| p.start_query(Q::NAME)); + let prof_timer = self.prof.query_provider(Q::NAME); let ((result, dep_node_index), diagnostics) = with_diagnostics(|diagnostics| { self.start_query(job.job.clone(), diagnostics, |tcx| { @@ -567,12 +532,7 @@ }) }); - self.sess.profiler(|p| p.end_query(Q::NAME)); - profq_msg!(self, ProfileQueriesMsg::ProviderEnd); - - if unlikely!(self.sess.opts.debugging_opts.query_dep_graph) { - self.dep_graph.mark_loaded_from_cache(dep_node_index, false); - } + drop(prof_timer); if unlikely!(!diagnostics.is_empty()) { if dep_node.kind != crate::dep_graph::DepKind::Null { @@ -614,19 +574,12 @@ let _ = self.get_query::<Q>(DUMMY_SP, key); } else { - profq_msg!(self, ProfileQueriesMsg::CacheHit); - self.sess.profiler(|p| p.record_query_hit(Q::NAME)); + self.prof.query_cache_hit(Q::NAME); } } #[allow(dead_code)] fn force_query<Q: QueryDescription<'tcx>>(self, key: Q::Key, span: Span, dep_node: DepNode) { - profq_msg!( - self, - ProfileQueriesMsg::QueryBegin(span.data(), - profq_query_msg!(Q::NAME.as_str(), self, key)) - ); - // We may be concurrently trying both execute and force a query. // Ensure that only one of them runs the query. let job = match JobOwner::try_get(self, span, &key) { @@ -643,7 +596,7 @@ macro_rules! handle_cycle_error { ([][$tcx: expr, $error:expr]) => {{ $tcx.report_cycle($error).emit(); - Value::from_cycle_error($tcx.global_tcx()) + Value::from_cycle_error($tcx) }}; ([fatal_cycle$(, $modifiers:ident)*][$tcx:expr, $error:expr]) => {{ $tcx.report_cycle($error).emit(); @@ -652,7 +605,7 @@ }}; ([cycle_delay_bug$(, $modifiers:ident)*][$tcx:expr, $error:expr]) => {{ $tcx.report_cycle($error).delay_as_bug(); - Value::from_cycle_error($tcx.global_tcx()) + Value::from_cycle_error($tcx) }}; ([$other:ident$(, $modifiers:ident)*][$($args:tt)*]) => { handle_cycle_error!([$($modifiers),*][$($args)*]) @@ -716,7 +669,6 @@ use rustc_data_structures::sharded::Sharded; use crate::{ rustc_data_structures::stable_hasher::HashStable, - rustc_data_structures::stable_hasher::StableHasherResult, rustc_data_structures::stable_hasher::StableHasher, ich::StableHashingContext }; @@ -859,7 +811,7 @@ } #[allow(nonstandard_style)] - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[derive(Clone, Copy)] pub enum QueryName { $($name),* } @@ -877,7 +829,7 @@ } #[allow(nonstandard_style)] - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] + #[derive(Clone, Debug)] pub enum Query<$tcx> { $($(#[$attr])* $name($K)),* } @@ -925,9 +877,7 @@ } impl<'a, $tcx> HashStable<StableHashingContext<'a>> for Query<$tcx> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { $(Query::$name(key) => key.hash_stable(hcx, hasher),)* @@ -999,7 +949,7 @@ // would be missing appropriate entries in `providers`. .unwrap_or(&tcx.queries.fallback_extern_providers) .$name; - provider(tcx.global_tcx(), key) + provider(tcx, key) }) } @@ -1194,37 +1144,6 @@ return false } - macro_rules! def_id { - () => { - if let Some(def_id) = dep_node.extract_def_id(tcx) { - def_id - } else { - // Return from the whole function. - return false - } - } - }; - - macro_rules! krate { - () => { (def_id!()).krate } - }; - - macro_rules! force_ex { - ($tcx:expr, $query:ident, $key:expr) => { - { - $tcx.force_query::<crate::ty::query::queries::$query<'_>>( - $key, - DUMMY_SP, - *dep_node - ); - } - } - }; - - macro_rules! force { - ($query:ident, $key:expr) => { force_ex!(tcx, $query, $key) } - }; - rustc_dep_node_force!([dep_node, tcx] // These are inputs that are expected to be pre-allocated and that // should therefore always be red or green already. @@ -1243,7 +1162,19 @@ bug!("force_from_dep_node: encountered {:?}", dep_node) } - DepKind::Analysis => { force!(analysis, krate!()); } + DepKind::Analysis => { + let def_id = if let Some(def_id) = dep_node.extract_def_id(tcx) { + def_id + } else { + // Return from the whole function. + return false + }; + tcx.force_query::<crate::ty::query::queries::analysis<'_>>( + def_id.krate, + DUMMY_SP, + *dep_node + ); + } ); true
diff --git a/src/librustc/ty/query/values.rs b/src/librustc/ty/query/values.rs index 0149f75..f0d1639 100644 --- a/src/librustc/ty/query/values.rs +++ b/src/librustc/ty/query/values.rs
@@ -1,7 +1,7 @@ use crate::ty::{self, Ty, TyCtxt, AdtSizedConstraint}; use crate::ty::util::NeedsDrop; -use syntax::symbol::InternedString; +use syntax::symbol::Symbol; pub(super) trait Value<'tcx>: Sized { fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self; @@ -22,7 +22,7 @@ impl<'tcx> Value<'tcx> for ty::SymbolName { fn from_cycle_error(_: TyCtxt<'tcx>) -> Self { - ty::SymbolName { name: InternedString::intern("<error>") } + ty::SymbolName { name: Symbol::intern("<error>") } } }
diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 565447d..1da65f4 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs
@@ -5,10 +5,10 @@ //! subtyping, type equality, etc. use crate::hir::def_id::DefId; -use crate::ty::subst::{Kind, UnpackedKind, SubstsRef}; +use crate::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; use crate::ty::error::{ExpectedFound, TypeError}; -use crate::mir::interpret::{ConstValue, Scalar}; +use crate::mir::interpret::{ConstValue, get_slice_bytes}; use std::rc::Rc; use std::iter; use rustc_target::spec::abi; @@ -349,7 +349,7 @@ ) -> RelateResult<'tcx, Ty<'tcx>> { let tcx = relation.tcx(); debug!("super_relate_tys: a={:?} b={:?}", a, b); - match (&a.sty, &b.sty) { + match (&a.kind, &b.kind) { (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { @@ -442,7 +442,7 @@ // the (anonymous) type of the same closure expression. So // all of their regions should be equated. let substs = relation.relate(&a_substs, &b_substs)?; - Ok(tcx.mk_closure(a_id, substs)) + Ok(tcx.mk_closure(a_id, &substs)) } (&ty::RawPtr(ref a_mt), &ty::RawPtr(ref b_mt)) => @@ -557,48 +557,62 @@ x.val }; - // Currently, the values that can be unified are those that - // implement both `PartialEq` and `Eq`, corresponding to - // `structural_match` types. - // FIXME(const_generics): check for `structural_match` synthetic attribute. - match (eagerly_eval(a), eagerly_eval(b)) { + // Currently, the values that can be unified are primitive types, + // and those that derive both `PartialEq` and `Eq`, corresponding + // to `structural_match` types. + let new_const_val = match (eagerly_eval(a), eagerly_eval(b)) { (ConstValue::Infer(_), _) | (_, ConstValue::Infer(_)) => { // The caller should handle these cases! bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b) } (ConstValue::Param(a_p), ConstValue::Param(b_p)) if a_p.index == b_p.index => { - Ok(a) + return Ok(a); } (ConstValue::Placeholder(p1), ConstValue::Placeholder(p2)) if p1 == p2 => { - Ok(a) + return Ok(a); } - (a_val @ ConstValue::Scalar(Scalar::Raw { .. }), b_val @ _) - if a.ty == b.ty && a_val == b_val => - { - Ok(tcx.mk_const(ty::Const { - val: a_val, - ty: a.ty, - })) + (ConstValue::Scalar(a_val), ConstValue::Scalar(b_val)) if a.ty == b.ty => { + if a_val == b_val { + Ok(ConstValue::Scalar(a_val)) + } else if let ty::FnPtr(_) = a.ty.kind { + let alloc_map = tcx.alloc_map.lock(); + let a_instance = alloc_map.unwrap_fn(a_val.to_ptr().unwrap().alloc_id); + let b_instance = alloc_map.unwrap_fn(b_val.to_ptr().unwrap().alloc_id); + if a_instance == b_instance { + Ok(ConstValue::Scalar(a_val)) + } else { + Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) + } + } else { + Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) + } } - // FIXME(const_generics): we should either handle `Scalar::Ptr` or add a comment - // saying that we're not handling it intentionally. + (a_val @ ConstValue::Slice { .. }, b_val @ ConstValue::Slice { .. }) => { + let a_bytes = get_slice_bytes(&tcx, a_val); + let b_bytes = get_slice_bytes(&tcx, b_val); + if a_bytes == b_bytes { + Ok(a_val) + } else { + Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) + } + } - // FIXME(const_generics): handle `ConstValue::ByRef` and `ConstValue::Slice`. + // FIXME(const_generics): handle `ConstValue::ByRef`. // FIXME(const_generics): this is wrong, as it is a projection (ConstValue::Unevaluated(a_def_id, a_substs), ConstValue::Unevaluated(b_def_id, b_substs)) if a_def_id == b_def_id => { - let substs = - relation.relate_with_variance(ty::Variance::Invariant, &a_substs, &b_substs)?; - Ok(tcx.mk_const(ty::Const { - val: ConstValue::Unevaluated(a_def_id, &substs), - ty: a.ty, - })) - } - - _ => Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))), - } + let substs = + relation.relate_with_variance(ty::Variance::Invariant, &a_substs, &b_substs)?; + Ok(ConstValue::Unevaluated(a_def_id, &substs)) + } + _ => Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))), + }; + new_const_val.map(|val| tcx.mk_const(ty::Const { + val, + ty: a.ty, + })) } impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::ExistentialPredicate<'tcx>> { @@ -711,29 +725,29 @@ } } -impl<'tcx> Relate<'tcx> for Kind<'tcx> { +impl<'tcx> Relate<'tcx> for GenericArg<'tcx> { fn relate<R: TypeRelation<'tcx>>( relation: &mut R, - a: &Kind<'tcx>, - b: &Kind<'tcx>, - ) -> RelateResult<'tcx, Kind<'tcx>> { + a: &GenericArg<'tcx>, + b: &GenericArg<'tcx>, + ) -> RelateResult<'tcx, GenericArg<'tcx>> { match (a.unpack(), b.unpack()) { - (UnpackedKind::Lifetime(a_lt), UnpackedKind::Lifetime(b_lt)) => { + (GenericArgKind::Lifetime(a_lt), GenericArgKind::Lifetime(b_lt)) => { Ok(relation.relate(&a_lt, &b_lt)?.into()) } - (UnpackedKind::Type(a_ty), UnpackedKind::Type(b_ty)) => { + (GenericArgKind::Type(a_ty), GenericArgKind::Type(b_ty)) => { Ok(relation.relate(&a_ty, &b_ty)?.into()) } - (UnpackedKind::Const(a_ct), UnpackedKind::Const(b_ct)) => { + (GenericArgKind::Const(a_ct), GenericArgKind::Const(b_ct)) => { Ok(relation.relate(&a_ct, &b_ct)?.into()) } - (UnpackedKind::Lifetime(unpacked), x) => { + (GenericArgKind::Lifetime(unpacked), x) => { bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } - (UnpackedKind::Type(unpacked), x) => { + (GenericArgKind::Type(unpacked), x) => { bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } - (UnpackedKind::Const(unpacked), x) => { + (GenericArgKind::Const(unpacked), x) => { bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } }
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index ec7cf1a..5d78d56 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs
@@ -9,7 +9,7 @@ use crate::ty::{self, Lift, Ty, TyCtxt, InferConst}; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::print::{FmtPrinter, Printer}; -use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use rustc_index::vec::{IndexVec, Idx}; use smallvec::SmallVec; use crate::mir::interpret; @@ -749,6 +749,7 @@ ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch), ConstMismatch(ref x) => return tcx.lift(x).map(ConstMismatch), IntrinsicCast => IntrinsicCast, + ObjectUnsafeCoercion(ref x) => return tcx.lift(x).map(ObjectUnsafeCoercion), }) } } @@ -761,6 +762,8 @@ Some(ty::InstanceDef::Item(def_id)), ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), + ty::InstanceDef::ReifyShim(def_id) => + Some(ty::InstanceDef::ReifyShim(def_id)), ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), ty::InstanceDef::FnPtrShim(def_id, ref ty) => @@ -966,6 +969,7 @@ def: match self.def { Item(did) => Item(did.fold_with(folder)), VtableShim(did) => VtableShim(did.fold_with(folder)), + ReifyShim(did) => ReifyShim(did.fold_with(folder)), Intrinsic(did) => Intrinsic(did.fold_with(folder)), FnPtrShim(did, ty) => FnPtrShim( did.fold_with(folder), @@ -994,7 +998,7 @@ use crate::ty::InstanceDef::*; self.substs.visit_with(visitor) || match self.def { - Item(did) | VtableShim(did) | Intrinsic(did) | Virtual(did, _) => { + Item(did) | VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { did.visit_with(visitor) }, FnPtrShim(did, ty) | CloneShim(did, ty) => { @@ -1023,7 +1027,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { - let sty = match self.sty { + let kind = match self.kind { ty::RawPtr(tm) => ty::RawPtr(tm.fold_with(folder)), ty::Array(typ, sz) => ty::Array(typ.fold_with(folder), sz.fold_with(folder)), ty::Slice(typ) => ty::Slice(typ.fold_with(folder)), @@ -1064,13 +1068,13 @@ ty::Bound(..) | ty::Placeholder(..) | ty::Never | - ty::Foreign(..) => return self + ty::Foreign(..) => return self, }; - if self.sty == sty { + if self.kind == kind { self } else { - folder.tcx().mk_ty(sty) + folder.tcx().mk_ty(kind) } } @@ -1079,7 +1083,7 @@ } fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { - match self.sty { + match self.kind { ty::RawPtr(ref tm) => tm.visit_with(visitor), ty::Array(typ, sz) => typ.visit_with(visitor) || sz.visit_with(visitor), ty::Slice(typ) => typ.visit_with(visitor), @@ -1215,16 +1219,23 @@ } } -BraceStructTypeFoldableImpl! { - impl<'tcx> TypeFoldable<'tcx> for ty::GenericPredicates<'tcx> { - parent, predicates - } -} - impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ty::Predicate<'tcx>> { fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { - let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>(); - folder.tcx().intern_predicates(&v) + // This code is hot enough that it's worth specializing for a list of + // length 0. (No other length is common enough to be worth singling + // out). + if self.len() == 0 { + self + } else { + // Don't bother interning if nothing changed, which is the common + // case. + let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>(); + if v[..] == self[..] { + self + } else { + folder.tcx().intern_predicates(&v) + } + } } fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { @@ -1340,6 +1351,7 @@ (ty::error::TypeError::ExistentialMismatch)(x), (ty::error::TypeError::ConstMismatch)(x), (ty::error::TypeError::IntrinsicCast), + (ty::error::TypeError::ObjectUnsafeCoercion)(x), } } @@ -1369,27 +1381,23 @@ impl<'tcx> TypeFoldable<'tcx> for ConstValue<'tcx> { fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { match *self { - ConstValue::ByRef { alloc, offset } => - ConstValue::ByRef { alloc, offset }, ConstValue::Infer(ic) => ConstValue::Infer(ic.fold_with(folder)), ConstValue::Param(p) => ConstValue::Param(p.fold_with(folder)), - ConstValue::Placeholder(p) => ConstValue::Placeholder(p), - ConstValue::Scalar(a) => ConstValue::Scalar(a), - ConstValue::Slice { data, start, end } => ConstValue::Slice { data, start, end }, ConstValue::Unevaluated(did, substs) => ConstValue::Unevaluated(did, substs.fold_with(folder)), + ConstValue::ByRef { .. } | ConstValue::Bound(..) | ConstValue::Placeholder(..) + | ConstValue::Scalar(..) | ConstValue::Slice { .. } => *self, + } } fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { match *self { - ConstValue::ByRef { .. } => false, ConstValue::Infer(ic) => ic.visit_with(visitor), ConstValue::Param(p) => p.visit_with(visitor), - ConstValue::Placeholder(_) => false, - ConstValue::Scalar(_) => false, - ConstValue::Slice { .. } => false, ConstValue::Unevaluated(_, substs) => substs.visit_with(visitor), + ConstValue::ByRef { .. } | ConstValue::Bound(..) | ConstValue::Placeholder(_) + | ConstValue::Scalar(_) | ConstValue::Slice { .. } => false, } } }
diff --git a/src/librustc/ty/structural_match.rs b/src/librustc/ty/structural_match.rs new file mode 100644 index 0000000..cdf5734 --- /dev/null +++ b/src/librustc/ty/structural_match.rs
@@ -0,0 +1,209 @@ +use crate::hir; +use rustc::infer::InferCtxt; +use rustc::traits::{self, ConstPatternStructural, TraitEngine}; +use rustc::traits::ObligationCause; + +use rustc_data_structures::fx::{FxHashSet}; + +use syntax_pos::Span; + +use crate::ty::{self, AdtDef, Ty, TyCtxt}; +use crate::ty::fold::{TypeFoldable, TypeVisitor}; + +#[derive(Debug)] +pub enum NonStructuralMatchTy<'tcx> { + Adt(&'tcx AdtDef), + Param, +} + +/// This method traverses the structure of `ty`, trying to find an +/// instance of an ADT (i.e. struct or enum) that was declared without +/// the `#[structural_match]` attribute, or a generic type parameter +/// (which cannot be determined to be `structural_match`). +/// +/// The "structure of a type" includes all components that would be +/// considered when doing a pattern match on a constant of that +/// type. +/// +/// * This means this method descends into fields of structs/enums, +/// and also descends into the inner type `T` of `&T` and `&mut T` +/// +/// * The traversal doesn't dereference unsafe pointers (`*const T`, +/// `*mut T`), and it does not visit the type arguments of an +/// instantiated generic like `PhantomData<T>`. +/// +/// The reason we do this search is Rust currently require all ADTs +/// reachable from a constant's type to be annotated with +/// `#[structural_match]`, an attribute which essentially says that +/// the implementation of `PartialEq::eq` behaves *equivalently* to a +/// comparison against the unfolded structure. +/// +/// For more background on why Rust has this requirement, and issues +/// that arose when the requirement was not enforced completely, see +/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. +pub fn search_for_structural_match_violation<'tcx>( + id: hir::HirId, + span: Span, + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option<NonStructuralMatchTy<'tcx>> { + // FIXME: we should instead pass in an `infcx` from the outside. + tcx.infer_ctxt().enter(|infcx| { + let mut search = Search { id, span, infcx, found: None, seen: FxHashSet::default() }; + ty.visit_with(&mut search); + search.found + }) +} + +/// This method returns true if and only if `adt_ty` itself has been marked as +/// eligible for structural-match: namely, if it implements both +/// `StructuralPartialEq` and `StructuralEq` (which are respectively injected by +/// `#[derive(PartialEq)]` and `#[derive(Eq)]`). +/// +/// Note that this does *not* recursively check if the substructure of `adt_ty` +/// implements the traits. +pub fn type_marked_structural(id: hir::HirId, + span: Span, + infcx: &InferCtxt<'_, 'tcx>, + adt_ty: Ty<'tcx>) + -> bool +{ + let mut fulfillment_cx = traits::FulfillmentContext::new(); + let cause = ObligationCause::new(span, id, ConstPatternStructural); + // require `#[derive(PartialEq)]` + let structural_peq_def_id = infcx.tcx.lang_items().structural_peq_trait().unwrap(); + fulfillment_cx.register_bound( + infcx, ty::ParamEnv::empty(), adt_ty, structural_peq_def_id, cause); + // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around + // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.) + let cause = ObligationCause::new(span, id, ConstPatternStructural); + let structural_teq_def_id = infcx.tcx.lang_items().structural_teq_trait().unwrap(); + fulfillment_cx.register_bound( + infcx, ty::ParamEnv::empty(), adt_ty, structural_teq_def_id, cause); + + // We deliberately skip *reporting* fulfillment errors (via + // `report_fulfillment_errors`), for two reasons: + // + // 1. The error messages would mention `std::marker::StructuralPartialEq` + // (a trait which is solely meant as an implementation detail + // for now), and + // + // 2. We are sometimes doing future-incompatibility lints for + // now, so we do not want unconditional errors here. + fulfillment_cx.select_all_or_error(infcx).is_ok() +} + +/// This implements the traversal over the structure of a given type to try to +/// find instances of ADTs (specifically structs or enums) that do not implement +/// the structural-match traits (`StructuralPartialEq` and `StructuralEq`). +struct Search<'a, 'tcx> { + id: hir::HirId, + span: Span, + + infcx: InferCtxt<'a, 'tcx>, + + /// Records first ADT that does not implement a structural-match trait. + found: Option<NonStructuralMatchTy<'tcx>>, + + /// Tracks ADTs previously encountered during search, so that + /// we will not recur on them again. + seen: FxHashSet<hir::def_id::DefId>, +} + +impl Search<'a, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool { + type_marked_structural(self.id, self.span, &self.infcx, adt_ty) + } +} + +impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + debug!("Search visiting ty: {:?}", ty); + + let (adt_def, substs) = match ty.kind { + ty::Adt(adt_def, substs) => (adt_def, substs), + ty::Param(_) => { + self.found = Some(NonStructuralMatchTy::Param); + return true; // Stop visiting. + } + ty::RawPtr(..) => { + // structural-match ignores substructure of + // `*const _`/`*mut _`, so skip `super_visit_with`. + // + // For example, if you have: + // ``` + // struct NonStructural; + // #[derive(PartialEq, Eq)] + // struct T(*const NonStructural); + // const C: T = T(std::ptr::null()); + // ``` + // + // Even though `NonStructural` does not implement `PartialEq`, + // structural equality on `T` does not recur into the raw + // pointer. Therefore, one can still use `C` in a pattern. + + // (But still tell caller to continue search.) + return false; + } + ty::FnDef(..) | ty::FnPtr(..) => { + // types of formals and return in `fn(_) -> _` are also irrelevant; + // so we do not recur into them via `super_visit_with` + // + // (But still tell caller to continue search.) + return false; + } + ty::Array(_, n) if { + n.try_eval_usize(self.tcx(), ty::ParamEnv::reveal_all()) == Some(0) + } => { + // rust-lang/rust#62336: ignore type of contents + // for empty array. + return false; + } + _ => { + ty.super_visit_with(self); + return false; + } + }; + + if !self.seen.insert(adt_def.did) { + debug!("Search already seen adt_def: {:?}", adt_def); + // let caller continue its search + return false; + } + + if !self.type_marked_structural(ty) { + debug!("Search found ty: {:?}", ty); + self.found = Some(NonStructuralMatchTy::Adt(&adt_def)); + return true; // Halt visiting! + } + + // structural-match does not care about the + // instantiation of the generics in an ADT (it + // instead looks directly at its fields outside + // this match), so we skip super_visit_with. + // + // (Must not recur on substs for `PhantomData<T>` cf + // rust-lang/rust#55028 and rust-lang/rust#55837; but also + // want to skip substs when only uses of generic are + // behind unsafe pointers `*const T`/`*mut T`.) + + // even though we skip super_visit_with, we must recur on + // fields of ADT. + let tcx = self.tcx(); + for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) { + if field_ty.visit_with(self) { + // found an ADT without structural-match; halt visiting! + assert!(self.found.is_some()); + return true; + } + } + + // Even though we do not want to recur on substs, we do + // want our caller to continue its own search. + false + } +}
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index e73a51e..3a9994a 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs
@@ -8,9 +8,9 @@ use crate::mir::interpret::ConstValue; use crate::middle::region; use polonius_engine::Atom; -use rustc_data_structures::indexed_vec::Idx; +use rustc_index::vec::Idx; use rustc_macros::HashStable; -use crate::ty::subst::{InternalSubsts, Subst, SubstsRef, Kind, UnpackedKind}; +use crate::ty::subst::{InternalSubsts, Subst, SubstsRef, GenericArg, GenericArgKind}; use crate::ty::{self, AdtDef, Discr, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFoldable}; use crate::ty::{List, TyS, ParamEnvAnd, ParamEnv}; use crate::ty::layout::VariantIdx; @@ -24,7 +24,7 @@ use std::ops::Range; use rustc_target::spec::abi; use syntax::ast::{self, Ident}; -use syntax::symbol::{kw, InternedString}; +use syntax::symbol::{kw, Symbol}; use self::InferTy::*; use self::TyKind::*; @@ -55,7 +55,7 @@ /// /// The `DefId` is needed to distinguish free regions in /// the event of shadowing. - BrNamed(DefId, InternedString), + BrNamed(DefId, Symbol), /// Anonymous region for the implicit env pointer parameter /// to a closure @@ -86,7 +86,7 @@ /// AST structure in `libsyntax/ast.rs` as well. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, HashStable, Debug)] -#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "TyKind")] +#[rustc_diagnostic_item = "TyKind"] pub enum TyKind<'tcx> { /// The primitive boolean type. Written as `bool`. Bool, @@ -158,11 +158,11 @@ /// The anonymous type of a closure. Used to represent the type of /// `|a| a`. - Closure(DefId, ClosureSubsts<'tcx>), + Closure(DefId, SubstsRef<'tcx>), /// The anonymous type of a generator. Used to represent the type of /// `|a| yield a`. - Generator(DefId, GeneratorSubsts<'tcx>, hir::GeneratorMovability), + Generator(DefId, SubstsRef<'tcx>, hir::GeneratorMovability), /// A type representin the types stored inside a generator. /// This should only appear in GeneratorInteriors. @@ -304,8 +304,7 @@ /// type parameters is similar, but the role of CK and CS are /// different. CK represents the "yield type" and CS represents the /// "return type" of the generator. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, - Debug, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, Debug)] pub struct ClosureSubsts<'tcx> { /// Lifetime and type parameters from the enclosing function, /// concatenated with the types of the upvars. @@ -320,7 +319,7 @@ struct SplitClosureSubsts<'tcx> { closure_kind_ty: Ty<'tcx>, closure_sig_ty: Ty<'tcx>, - upvar_kinds: &'tcx [Kind<'tcx>], + upvar_kinds: &'tcx [GenericArg<'tcx>], } impl<'tcx> ClosureSubsts<'tcx> { @@ -345,7 +344,7 @@ ) -> impl Iterator<Item = Ty<'tcx>> + 'tcx { let SplitClosureSubsts { upvar_kinds, .. } = self.split(def_id, tcx); upvar_kinds.iter().map(|t| { - if let UnpackedKind::Type(ty) = t.unpack() { + if let GenericArgKind::Type(ty) = t.unpack() { ty } else { bug!("upvar should be type") @@ -356,7 +355,7 @@ /// Returns the closure kind for this closure; may return a type /// variable during inference. To get the closure kind during /// inference, use `infcx.closure_kind(def_id, substs)`. - pub fn closure_kind_ty(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> { + pub fn kind_ty(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> { self.split(def_id, tcx).closure_kind_ty } @@ -364,7 +363,7 @@ /// closure; may contain type variables during inference. To get /// the closure signature during inference, use /// `infcx.fn_sig(def_id)`. - pub fn closure_sig_ty(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> { + pub fn sig_ty(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> { self.split(def_id, tcx).closure_sig_ty } @@ -373,7 +372,7 @@ /// there are no type variables. /// /// If you have an inference context, use `infcx.closure_kind()`. - pub fn closure_kind(self, def_id: DefId, tcx: TyCtxt<'tcx>) -> ty::ClosureKind { + pub fn kind(self, def_id: DefId, tcx: TyCtxt<'tcx>) -> ty::ClosureKind { self.split(def_id, tcx).closure_kind_ty.to_opt_closure_kind().unwrap() } @@ -382,18 +381,17 @@ /// there are no type variables. /// /// If you have an inference context, use `infcx.closure_sig()`. - pub fn closure_sig(self, def_id: DefId, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { - let ty = self.closure_sig_ty(def_id, tcx); - match ty.sty { + pub fn sig(&self, def_id: DefId, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { + let ty = self.sig_ty(def_id, tcx); + match ty.kind { ty::FnPtr(sig) => sig, - _ => bug!("closure_sig_ty is not a fn-ptr: {:?}", ty.sty), + _ => bug!("closure_sig_ty is not a fn-ptr: {:?}", ty.kind), } } } /// Similar to `ClosureSubsts`; see the above documentation for more. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, - RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, Debug)] pub struct GeneratorSubsts<'tcx> { pub substs: SubstsRef<'tcx>, } @@ -402,7 +400,7 @@ yield_ty: Ty<'tcx>, return_ty: Ty<'tcx>, witness: Ty<'tcx>, - upvar_kinds: &'tcx [Kind<'tcx>], + upvar_kinds: &'tcx [GenericArg<'tcx>], } impl<'tcx> GeneratorSubsts<'tcx> { @@ -434,7 +432,7 @@ ) -> impl Iterator<Item = Ty<'tcx>> + 'tcx { let SplitGeneratorSubsts { upvar_kinds, .. } = self.split(def_id, tcx); upvar_kinds.iter().map(|t| { - if let UnpackedKind::Type(ty) = t.unpack() { + if let GenericArgKind::Type(ty) = t.unpack() { ty } else { bug!("upvar should be type") @@ -511,7 +509,7 @@ /// variant indices. #[inline] pub fn discriminants( - &'tcx self, + self, def_id: DefId, tcx: TyCtxt<'tcx>, ) -> impl Iterator<Item = (VariantIdx, Discr<'tcx>)> + Captures<'tcx> { @@ -523,7 +521,7 @@ /// Calls `f` with a reference to the name of the enumerator for the given /// variant `v`. #[inline] - pub fn variant_name(&self, v: VariantIdx) -> Cow<'static, str> { + pub fn variant_name(self, v: VariantIdx) -> Cow<'static, str> { match v.as_usize() { Self::UNRESUMED => Cow::from(Self::UNRESUMED_NAME), Self::RETURNED => Cow::from(Self::RETURNED_NAME), @@ -568,8 +566,8 @@ #[derive(Debug, Copy, Clone)] pub enum UpvarSubsts<'tcx> { - Closure(ClosureSubsts<'tcx>), - Generator(GeneratorSubsts<'tcx>), + Closure(SubstsRef<'tcx>), + Generator(SubstsRef<'tcx>), } impl<'tcx> UpvarSubsts<'tcx> { @@ -577,14 +575,14 @@ pub fn upvar_tys( self, def_id: DefId, - tcx: TyCtxt<'_>, + tcx: TyCtxt<'tcx>, ) -> impl Iterator<Item = Ty<'tcx>> + 'tcx { let upvar_kinds = match self { - UpvarSubsts::Closure(substs) => substs.split(def_id, tcx).upvar_kinds, - UpvarSubsts::Generator(substs) => substs.split(def_id, tcx).upvar_kinds, + UpvarSubsts::Closure(substs) => substs.as_closure().split(def_id, tcx).upvar_kinds, + UpvarSubsts::Generator(substs) => substs.as_generator().split(def_id, tcx).upvar_kinds, }; upvar_kinds.iter().map(|t| { - if let UnpackedKind::Type(ty) = t.unpack() { + if let GenericArgKind::Type(ty) = t.unpack() { ty } else { bug!("upvar should be type") @@ -1035,7 +1033,7 @@ } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, Debug)] pub struct GenSig<'tcx> { pub yield_ty: Ty<'tcx>, pub return_ty: Ty<'tcx>, @@ -1123,16 +1121,16 @@ Hash, RustcEncodable, RustcDecodable, HashStable)] pub struct ParamTy { pub index: u32, - pub name: InternedString, + pub name: Symbol, } impl<'tcx> ParamTy { - pub fn new(index: u32, name: InternedString) -> ParamTy { + pub fn new(index: u32, name: Symbol) -> ParamTy { ParamTy { index, name: name } } pub fn for_self() -> ParamTy { - ParamTy::new(0, kw::SelfUpper.as_interned_str()) + ParamTy::new(0, kw::SelfUpper) } pub fn for_def(def: &ty::GenericParamDef) -> ParamTy { @@ -1148,11 +1146,11 @@ Eq, PartialEq, Ord, PartialOrd, HashStable)] pub struct ParamConst { pub index: u32, - pub name: InternedString, + pub name: Symbol, } impl<'tcx> ParamConst { - pub fn new(index: u32, name: InternedString) -> ParamConst { + pub fn new(index: u32, name: Symbol) -> ParamConst { ParamConst { index, name } } @@ -1165,7 +1163,7 @@ } } -newtype_index! { +rustc_index::newtype_index! { /// A [De Bruijn index][dbi] is a standard means of representing /// regions (and perhaps later types) in a higher-ranked setting. In /// particular, imagine a type like this: @@ -1325,7 +1323,7 @@ pub struct EarlyBoundRegion { pub def_id: DefId, pub index: u32, - pub name: InternedString, + pub name: Symbol, } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] @@ -1349,7 +1347,7 @@ pub index: u32, } -newtype_index! { +rustc_index::newtype_index! { pub struct RegionVid { DEBUG_FORMAT = custom, } @@ -1376,7 +1374,7 @@ FreshFloatTy(u32), } -newtype_index! { +rustc_index::newtype_index! { pub struct BoundVar { .. } } @@ -1389,7 +1387,7 @@ #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] pub enum BoundTyKind { Anon, - Param(InternedString), + Param(Symbol), } impl_stable_hash_for!(struct BoundTy { var, kind }); @@ -1678,7 +1676,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_unit(&self) -> bool { - match self.sty { + match self.kind { Tuple(ref tys) => tys.is_empty(), _ => false, } @@ -1686,7 +1684,7 @@ #[inline] pub fn is_never(&self) -> bool { - match self.sty { + match self.kind { Never => true, _ => false, } @@ -1701,7 +1699,7 @@ pub fn conservative_is_privately_uninhabited(&self, tcx: TyCtxt<'tcx>) -> bool { // FIXME(varkor): we can make this less conversative by substituting concrete // type arguments. - match self.sty { + match self.kind { ty::Never => true, ty::Adt(def, _) if def.is_union() => { // For now, `union`s are never considered uninhabited. @@ -1741,7 +1739,7 @@ #[inline] pub fn is_primitive(&self) -> bool { - match self.sty { + match self.kind { Bool | Char | Int(_) | Uint(_) | Float(_) => true, _ => false, } @@ -1749,7 +1747,7 @@ #[inline] pub fn is_ty_var(&self) -> bool { - match self.sty { + match self.kind { Infer(TyVar(_)) => true, _ => false, } @@ -1757,7 +1755,7 @@ #[inline] pub fn is_ty_infer(&self) -> bool { - match self.sty { + match self.kind { Infer(_) => true, _ => false, } @@ -1765,7 +1763,7 @@ #[inline] pub fn is_phantom_data(&self) -> bool { - if let Adt(def, _) = self.sty { + if let Adt(def, _) = self.kind { def.is_phantom_data() } else { false @@ -1773,11 +1771,15 @@ } #[inline] - pub fn is_bool(&self) -> bool { self.sty == Bool } + pub fn is_bool(&self) -> bool { self.kind == Bool } + + /// Returns `true` if this type is a `str`. + #[inline] + pub fn is_str(&self) -> bool { self.kind == Str } #[inline] pub fn is_param(&self, index: u32) -> bool { - match self.sty { + match self.kind { ty::Param(ref data) => data.index == index, _ => false, } @@ -1785,8 +1787,8 @@ #[inline] pub fn is_slice(&self) -> bool { - match self.sty { - RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => match ty.sty { + match self.kind { + RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => match ty.kind { Slice(_) | Str => true, _ => false, }, @@ -1796,14 +1798,14 @@ #[inline] pub fn is_simd(&self) -> bool { - match self.sty { + match self.kind { Adt(def, _) => def.repr.simd(), _ => false, } } pub fn sequence_element_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.sty { + match self.kind { Array(ty, _) | Slice(ty) => ty, Str => tcx.mk_mach_uint(ast::UintTy::U8), _ => bug!("sequence_element_type called on non-sequence value: {}", self), @@ -1811,7 +1813,7 @@ } pub fn simd_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.sty { + match self.kind { Adt(def, substs) => { def.non_enum_variant().fields[0].ty(tcx, substs) } @@ -1820,7 +1822,7 @@ } pub fn simd_size(&self, _cx: TyCtxt<'_>) -> usize { - match self.sty { + match self.kind { Adt(def, _) => def.non_enum_variant().fields.len(), _ => bug!("simd_size called on invalid type") } @@ -1828,7 +1830,7 @@ #[inline] pub fn is_region_ptr(&self) -> bool { - match self.sty { + match self.kind { Ref(..) => true, _ => false, } @@ -1836,7 +1838,7 @@ #[inline] pub fn is_mutable_ptr(&self) -> bool { - match self.sty { + match self.kind { RawPtr(TypeAndMut { mutbl: hir::Mutability::MutMutable, .. }) | Ref(_, _, hir::Mutability::MutMutable) => true, _ => false @@ -1845,7 +1847,7 @@ #[inline] pub fn is_unsafe_ptr(&self) -> bool { - match self.sty { + match self.kind { RawPtr(_) => return true, _ => return false, } @@ -1860,7 +1862,7 @@ /// Returns `true` if this type is an `Arc<T>`. #[inline] pub fn is_arc(&self) -> bool { - match self.sty { + match self.kind { Adt(def, _) => def.is_arc(), _ => false, } @@ -1869,7 +1871,7 @@ /// Returns `true` if this type is an `Rc<T>`. #[inline] pub fn is_rc(&self) -> bool { - match self.sty { + match self.kind { Adt(def, _) => def.is_rc(), _ => false, } @@ -1877,7 +1879,7 @@ #[inline] pub fn is_box(&self) -> bool { - match self.sty { + match self.kind { Adt(def, _) => def.is_box(), _ => false, } @@ -1885,7 +1887,7 @@ /// panics if called on any type other than `Box<T>` pub fn boxed_ty(&self) -> Ty<'tcx> { - match self.sty { + match self.kind { Adt(def, substs) if def.is_box() => substs.type_at(0), _ => bug!("`boxed_ty` is called on non-box type {:?}", self), } @@ -1896,7 +1898,7 @@ /// contents are abstract to rustc.) #[inline] pub fn is_scalar(&self) -> bool { - match self.sty { + match self.kind { Bool | Char | Int(_) | Float(_) | Uint(_) | Infer(IntVar(_)) | Infer(FloatVar(_)) | FnDef(..) | FnPtr(_) | RawPtr(_) => true, @@ -1907,7 +1909,7 @@ /// Returns `true` if this type is a floating point type. #[inline] pub fn is_floating_point(&self) -> bool { - match self.sty { + match self.kind { Float(_) | Infer(FloatVar(_)) => true, _ => false, @@ -1916,7 +1918,7 @@ #[inline] pub fn is_trait(&self) -> bool { - match self.sty { + match self.kind { Dynamic(..) => true, _ => false, } @@ -1924,7 +1926,7 @@ #[inline] pub fn is_enum(&self) -> bool { - match self.sty { + match self.kind { Adt(adt_def, _) => { adt_def.is_enum() } @@ -1934,7 +1936,7 @@ #[inline] pub fn is_closure(&self) -> bool { - match self.sty { + match self.kind { Closure(..) => true, _ => false, } @@ -1942,7 +1944,7 @@ #[inline] pub fn is_generator(&self) -> bool { - match self.sty { + match self.kind { Generator(..) => true, _ => false, } @@ -1950,7 +1952,7 @@ #[inline] pub fn is_integral(&self) -> bool { - match self.sty { + match self.kind { Infer(IntVar(_)) | Int(_) | Uint(_) => true, _ => false } @@ -1958,7 +1960,7 @@ #[inline] pub fn is_fresh_ty(&self) -> bool { - match self.sty { + match self.kind { Infer(FreshTy(_)) => true, _ => false, } @@ -1966,7 +1968,7 @@ #[inline] pub fn is_fresh(&self) -> bool { - match self.sty { + match self.kind { Infer(FreshTy(_)) => true, Infer(FreshIntTy(_)) => true, Infer(FreshFloatTy(_)) => true, @@ -1976,7 +1978,7 @@ #[inline] pub fn is_char(&self) -> bool { - match self.sty { + match self.kind { Char => true, _ => false, } @@ -1989,7 +1991,7 @@ #[inline] pub fn is_signed(&self) -> bool { - match self.sty { + match self.kind { Int(_) => true, _ => false, } @@ -1997,7 +1999,7 @@ #[inline] pub fn is_ptr_sized_integral(&self) -> bool { - match self.sty { + match self.kind { Int(ast::IntTy::Isize) | Uint(ast::UintTy::Usize) => true, _ => false, } @@ -2005,7 +2007,7 @@ #[inline] pub fn is_machine(&self) -> bool { - match self.sty { + match self.kind { Int(..) | Uint(..) | Float(..) => true, _ => false, } @@ -2013,7 +2015,7 @@ #[inline] pub fn has_concrete_skeleton(&self) -> bool { - match self.sty { + match self.kind { Param(_) | Infer(_) | Error => false, _ => true, } @@ -2024,7 +2026,7 @@ /// The parameter `explicit` indicates if this is an *explicit* dereference. /// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly. pub fn builtin_deref(&self, explicit: bool) -> Option<TypeAndMut<'tcx>> { - match self.sty { + match self.kind { Adt(def, _) if def.is_box() => { Some(TypeAndMut { ty: self.boxed_ty(), @@ -2039,14 +2041,14 @@ /// Returns the type of `ty[i]`. pub fn builtin_index(&self) -> Option<Ty<'tcx>> { - match self.sty { + match self.kind { Array(ty, _) | Slice(ty) => Some(ty), _ => None, } } pub fn fn_sig(&self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> { - match self.sty { + match self.kind { FnDef(def_id, substs) => { tcx.fn_sig(def_id).subst(tcx, substs) } @@ -2063,7 +2065,7 @@ #[inline] pub fn is_fn(&self) -> bool { - match self.sty { + match self.kind { FnDef(..) | FnPtr(_) => true, _ => false, } @@ -2071,7 +2073,7 @@ #[inline] pub fn is_fn_ptr(&self) -> bool { - match self.sty { + match self.kind { FnPtr(_) => true, _ => false, } @@ -2079,7 +2081,7 @@ #[inline] pub fn is_impl_trait(&self) -> bool { - match self.sty { + match self.kind { Opaque(..) => true, _ => false, } @@ -2087,7 +2089,7 @@ #[inline] pub fn ty_adt_def(&self) -> Option<&'tcx AdtDef> { - match self.sty { + match self.kind { Adt(adt, _) => Some(adt), _ => None, } @@ -2096,7 +2098,7 @@ /// Iterates over tuple fields. /// Panics when called on anything but a tuple. pub fn tuple_fields(&self) -> impl DoubleEndedIterator<Item=Ty<'tcx>> { - match self.sty { + match self.kind { Tuple(substs) => substs.iter().map(|field| field.expect_ty()), _ => bug!("tuple_fields called on non-tuple"), } @@ -2106,9 +2108,10 @@ /// FIXME This requires the optimized MIR in the case of generators. #[inline] pub fn variant_range(&self, tcx: TyCtxt<'tcx>) -> Option<Range<VariantIdx>> { - match self.sty { + match self.kind { TyKind::Adt(adt, _) => Some(adt.variant_range()), - TyKind::Generator(def_id, substs, _) => Some(substs.variant_range(def_id, tcx)), + TyKind::Generator(def_id, substs, _) => + Some(substs.as_generator().variant_range(def_id, tcx)), _ => None, } } @@ -2122,10 +2125,10 @@ tcx: TyCtxt<'tcx>, variant_index: VariantIdx, ) -> Option<Discr<'tcx>> { - match self.sty { + match self.kind { TyKind::Adt(adt, _) => Some(adt.discriminant_for_variant(tcx, variant_index)), TyKind::Generator(def_id, substs, _) => - Some(substs.discriminant_for_variant(def_id, tcx, variant_index)), + Some(substs.as_generator().discriminant_for_variant(def_id, tcx, variant_index)), _ => None, } } @@ -2134,7 +2137,7 @@ /// types reachable from this type via `walk_tys`). This ignores late-bound /// regions binders. pub fn push_regions(&self, out: &mut SmallVec<[ty::Region<'tcx>; 4]>) { - match self.sty { + match self.kind { Ref(region, _, _) => { out.push(region); } @@ -2147,8 +2150,8 @@ Adt(_, substs) | Opaque(_, substs) => { out.extend(substs.regions()) } - Closure(_, ClosureSubsts { ref substs }) | - Generator(_, GeneratorSubsts { ref substs }, _) => { + Closure(_, ref substs ) | + Generator(_, ref substs, _) => { out.extend(substs.regions()) } Projection(ref data) | UnnormalizedProjection(ref data) => { @@ -2190,7 +2193,7 @@ /// inferred. Once upvar inference (in `src/librustc_typeck/check/upvar.rs`) /// is complete, that type variable will be unified. pub fn to_opt_closure_kind(&self) -> Option<ty::ClosureKind> { - match self.sty { + match self.kind { Int(int_ty) => match int_ty { ast::IntTy::I8 => Some(ty::ClosureKind::Fn), ast::IntTy::I16 => Some(ty::ClosureKind::FnMut), @@ -2198,7 +2201,9 @@ _ => bug!("cannot convert type `{:?}` to a closure kind", self), }, - Infer(_) => None, + // "Bound" types appear in canonical queries when the + // closure type is not yet known + Bound(..) | Infer(_) => None, Error => Some(ty::ClosureKind::Fn), @@ -2211,7 +2216,7 @@ /// Returning true means the type is known to be sized. Returning /// `false` means nothing -- could be sized, might not be. pub fn is_trivially_sized(&self, tcx: TyCtxt<'tcx>) -> bool { - match self.sty { + match self.kind { ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Float(_) | ty::FnDef(..) | ty::FnPtr(_) | ty::RawPtr(..) | @@ -2298,8 +2303,8 @@ ty: Ty<'tcx>, ) -> Option<u128> { assert_eq!(self.ty, ty); - // if `ty` does not depend on generic parameters, use an empty param_env let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size; + // if `ty` does not depend on generic parameters, use an empty param_env self.eval(tcx, param_env).val.try_to_bits(size) } @@ -2366,6 +2371,4 @@ Var(ConstVid<'tcx>), /// A fresh const variable. See `infer::freshen` for more details. Fresh(u32), - /// Canonicalized const variable, used only when preparing a trait query. - Canonical(DebruijnIndex, BoundVar), }
diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs index ea829da..2972197 100644 --- a/src/librustc/ty/subst.rs +++ b/src/librustc/ty/subst.rs
@@ -2,9 +2,10 @@ use crate::hir::def_id::DefId; use crate::infer::canonical::Canonical; -use crate::ty::{self, Lift, List, Ty, TyCtxt, InferConst, ParamConst}; +use crate::ty::{self, Lift, List, Ty, TyCtxt, ParamConst}; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use crate::mir::interpret::ConstValue; +use crate::ty::sty::{ClosureSubsts, GeneratorSubsts}; use rustc_serialize::{self, Encodable, Encoder, Decodable, Decoder}; use syntax_pos::{Span, DUMMY_SP}; @@ -20,11 +21,11 @@ /// An entity in the Rust type system, which can be one of /// several kinds (types, lifetimes, and consts). -/// To reduce memory usage, a `Kind` is a interned pointer, +/// To reduce memory usage, a `GenericArg` is a interned pointer, /// with the lowest 2 bits being reserved for a tag to /// indicate the type (`Ty`, `Region`, or `Const`) it points to. #[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct Kind<'tcx> { +pub struct GenericArg<'tcx> { ptr: NonZeroUsize, marker: PhantomData<(Ty<'tcx>, ty::Region<'tcx>, &'tcx ty::Const<'tcx>)> } @@ -35,33 +36,33 @@ const CONST_TAG: usize = 0b10; #[derive(Debug, RustcEncodable, RustcDecodable, PartialEq, Eq, PartialOrd, Ord, HashStable)] -pub enum UnpackedKind<'tcx> { +pub enum GenericArgKind<'tcx> { Lifetime(ty::Region<'tcx>), Type(Ty<'tcx>), Const(&'tcx ty::Const<'tcx>), } -impl<'tcx> UnpackedKind<'tcx> { - fn pack(self) -> Kind<'tcx> { +impl<'tcx> GenericArgKind<'tcx> { + fn pack(self) -> GenericArg<'tcx> { let (tag, ptr) = match self { - UnpackedKind::Lifetime(lt) => { + GenericArgKind::Lifetime(lt) => { // Ensure we can use the tag bits. assert_eq!(mem::align_of_val(lt) & TAG_MASK, 0); (REGION_TAG, lt as *const _ as usize) } - UnpackedKind::Type(ty) => { + GenericArgKind::Type(ty) => { // Ensure we can use the tag bits. assert_eq!(mem::align_of_val(ty) & TAG_MASK, 0); (TYPE_TAG, ty as *const _ as usize) } - UnpackedKind::Const(ct) => { + GenericArgKind::Const(ct) => { // Ensure we can use the tag bits. assert_eq!(mem::align_of_val(ct) & TAG_MASK, 0); (CONST_TAG, ct as *const _ as usize) } }; - Kind { + GenericArg { ptr: unsafe { NonZeroUsize::new_unchecked(ptr | tag) }, @@ -70,119 +71,137 @@ } } -impl fmt::Debug for Kind<'tcx> { +impl fmt::Debug for GenericArg<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.unpack() { - UnpackedKind::Lifetime(lt) => lt.fmt(f), - UnpackedKind::Type(ty) => ty.fmt(f), - UnpackedKind::Const(ct) => ct.fmt(f), + GenericArgKind::Lifetime(lt) => lt.fmt(f), + GenericArgKind::Type(ty) => ty.fmt(f), + GenericArgKind::Const(ct) => ct.fmt(f), } } } -impl<'tcx> Ord for Kind<'tcx> { - fn cmp(&self, other: &Kind<'_>) -> Ordering { +impl<'tcx> Ord for GenericArg<'tcx> { + fn cmp(&self, other: &GenericArg<'_>) -> Ordering { self.unpack().cmp(&other.unpack()) } } -impl<'tcx> PartialOrd for Kind<'tcx> { - fn partial_cmp(&self, other: &Kind<'_>) -> Option<Ordering> { +impl<'tcx> PartialOrd for GenericArg<'tcx> { + fn partial_cmp(&self, other: &GenericArg<'_>) -> Option<Ordering> { Some(self.cmp(&other)) } } -impl<'tcx> From<ty::Region<'tcx>> for Kind<'tcx> { - fn from(r: ty::Region<'tcx>) -> Kind<'tcx> { - UnpackedKind::Lifetime(r).pack() +impl<'tcx> From<ty::Region<'tcx>> for GenericArg<'tcx> { + fn from(r: ty::Region<'tcx>) -> GenericArg<'tcx> { + GenericArgKind::Lifetime(r).pack() } } -impl<'tcx> From<Ty<'tcx>> for Kind<'tcx> { - fn from(ty: Ty<'tcx>) -> Kind<'tcx> { - UnpackedKind::Type(ty).pack() +impl<'tcx> From<Ty<'tcx>> for GenericArg<'tcx> { + fn from(ty: Ty<'tcx>) -> GenericArg<'tcx> { + GenericArgKind::Type(ty).pack() } } -impl<'tcx> From<&'tcx ty::Const<'tcx>> for Kind<'tcx> { - fn from(c: &'tcx ty::Const<'tcx>) -> Kind<'tcx> { - UnpackedKind::Const(c).pack() +impl<'tcx> From<&'tcx ty::Const<'tcx>> for GenericArg<'tcx> { + fn from(c: &'tcx ty::Const<'tcx>) -> GenericArg<'tcx> { + GenericArgKind::Const(c).pack() } } -impl<'tcx> Kind<'tcx> { +impl<'tcx> GenericArg<'tcx> { #[inline] - pub fn unpack(self) -> UnpackedKind<'tcx> { + pub fn unpack(self) -> GenericArgKind<'tcx> { let ptr = self.ptr.get(); unsafe { match ptr & TAG_MASK { - REGION_TAG => UnpackedKind::Lifetime(&*((ptr & !TAG_MASK) as *const _)), - TYPE_TAG => UnpackedKind::Type(&*((ptr & !TAG_MASK) as *const _)), - CONST_TAG => UnpackedKind::Const(&*((ptr & !TAG_MASK) as *const _)), + REGION_TAG => GenericArgKind::Lifetime(&*((ptr & !TAG_MASK) as *const _)), + TYPE_TAG => GenericArgKind::Type(&*((ptr & !TAG_MASK) as *const _)), + CONST_TAG => GenericArgKind::Const(&*((ptr & !TAG_MASK) as *const _)), _ => intrinsics::unreachable() } } } - /// Unpack the `Kind` as a type when it is known certainly to be a type. + /// Unpack the `GenericArg` as a type when it is known certainly to be a type. /// This is true in cases where `Substs` is used in places where the kinds are known /// to be limited (e.g. in tuples, where the only parameters are type parameters). pub fn expect_ty(self) -> Ty<'tcx> { match self.unpack() { - UnpackedKind::Type(ty) => ty, + GenericArgKind::Type(ty) => ty, _ => bug!("expected a type, but found another kind"), } } } -impl<'a, 'tcx> Lift<'tcx> for Kind<'a> { - type Lifted = Kind<'tcx>; +impl<'a, 'tcx> Lift<'tcx> for GenericArg<'a> { + type Lifted = GenericArg<'tcx>; fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> { match self.unpack() { - UnpackedKind::Lifetime(lt) => tcx.lift(<).map(|lt| lt.into()), - UnpackedKind::Type(ty) => tcx.lift(&ty).map(|ty| ty.into()), - UnpackedKind::Const(ct) => tcx.lift(&ct).map(|ct| ct.into()), + GenericArgKind::Lifetime(lt) => tcx.lift(<).map(|lt| lt.into()), + GenericArgKind::Type(ty) => tcx.lift(&ty).map(|ty| ty.into()), + GenericArgKind::Const(ct) => tcx.lift(&ct).map(|ct| ct.into()), } } } -impl<'tcx> TypeFoldable<'tcx> for Kind<'tcx> { +impl<'tcx> TypeFoldable<'tcx> for GenericArg<'tcx> { fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { match self.unpack() { - UnpackedKind::Lifetime(lt) => lt.fold_with(folder).into(), - UnpackedKind::Type(ty) => ty.fold_with(folder).into(), - UnpackedKind::Const(ct) => ct.fold_with(folder).into(), + GenericArgKind::Lifetime(lt) => lt.fold_with(folder).into(), + GenericArgKind::Type(ty) => ty.fold_with(folder).into(), + GenericArgKind::Const(ct) => ct.fold_with(folder).into(), } } fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { match self.unpack() { - UnpackedKind::Lifetime(lt) => lt.visit_with(visitor), - UnpackedKind::Type(ty) => ty.visit_with(visitor), - UnpackedKind::Const(ct) => ct.visit_with(visitor), + GenericArgKind::Lifetime(lt) => lt.visit_with(visitor), + GenericArgKind::Type(ty) => ty.visit_with(visitor), + GenericArgKind::Const(ct) => ct.visit_with(visitor), } } } -impl<'tcx> Encodable for Kind<'tcx> { +impl<'tcx> Encodable for GenericArg<'tcx> { fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> { self.unpack().encode(e) } } -impl<'tcx> Decodable for Kind<'tcx> { - fn decode<D: Decoder>(d: &mut D) -> Result<Kind<'tcx>, D::Error> { - Ok(UnpackedKind::decode(d)?.pack()) +impl<'tcx> Decodable for GenericArg<'tcx> { + fn decode<D: Decoder>(d: &mut D) -> Result<GenericArg<'tcx>, D::Error> { + Ok(GenericArgKind::decode(d)?.pack()) } } /// A substitution mapping generic parameters to new values. -pub type InternalSubsts<'tcx> = List<Kind<'tcx>>; +pub type InternalSubsts<'tcx> = List<GenericArg<'tcx>>; pub type SubstsRef<'tcx> = &'tcx InternalSubsts<'tcx>; impl<'a, 'tcx> InternalSubsts<'tcx> { + /// Interpret these substitutions as the substitutions of a closure type. + /// Closure substitutions have a particular structure controlled by the + /// compiler that encodes information like the signature and closure kind; + /// see `ty::ClosureSubsts` struct for more comments. + pub fn as_closure(&'a self) -> ClosureSubsts<'a> { + ClosureSubsts { + substs: self, + } + } + + /// Interpret these substitutions as the substitutions of a generator type. + /// Closure substitutions have a particular structure controlled by the + /// compiler that encodes information like the signature and generator kind; + /// see `ty::GeneratorSubsts` struct for more comments. + pub fn as_generator(&'tcx self) -> GeneratorSubsts<'tcx> { + GeneratorSubsts { substs: self } + } + /// Creates a `InternalSubsts` that maps each generic parameter to itself. pub fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> { Self::for_item(tcx, def_id, |param, _| { @@ -215,9 +234,7 @@ ty::GenericParamDefKind::Const => { tcx.mk_const(ty::Const { - val: ConstValue::Infer( - InferConst::Canonical(ty::INNERMOST, ty::BoundVar::from(param.index)) - ), + val: ConstValue::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)), ty: tcx.type_of(def_id), }).into() } @@ -232,7 +249,7 @@ /// substitute defaults of generic parameters. pub fn for_item<F>(tcx: TyCtxt<'tcx>, def_id: DefId, mut mk_kind: F) -> SubstsRef<'tcx> where - F: FnMut(&ty::GenericParamDef, &[Kind<'tcx>]) -> Kind<'tcx>, + F: FnMut(&ty::GenericParamDef, &[GenericArg<'tcx>]) -> GenericArg<'tcx>, { let defs = tcx.generics_of(def_id); let count = defs.count(); @@ -243,7 +260,7 @@ pub fn extend_to<F>(&self, tcx: TyCtxt<'tcx>, def_id: DefId, mut mk_kind: F) -> SubstsRef<'tcx> where - F: FnMut(&ty::GenericParamDef, &[Kind<'tcx>]) -> Kind<'tcx>, + F: FnMut(&ty::GenericParamDef, &[GenericArg<'tcx>]) -> GenericArg<'tcx>, { Self::for_item(tcx, def_id, |param, substs| { self.get(param.index as usize) @@ -253,12 +270,12 @@ } fn fill_item<F>( - substs: &mut SmallVec<[Kind<'tcx>; 8]>, + substs: &mut SmallVec<[GenericArg<'tcx>; 8]>, tcx: TyCtxt<'tcx>, defs: &ty::Generics, mk_kind: &mut F, ) where - F: FnMut(&ty::GenericParamDef, &[Kind<'tcx>]) -> Kind<'tcx>, + F: FnMut(&ty::GenericParamDef, &[GenericArg<'tcx>]) -> GenericArg<'tcx>, { if let Some(def_id) = defs.parent { let parent_defs = tcx.generics_of(def_id); @@ -267,10 +284,10 @@ Self::fill_single(substs, defs, mk_kind) } - fn fill_single<F>(substs: &mut SmallVec<[Kind<'tcx>; 8]>, + fn fill_single<F>(substs: &mut SmallVec<[GenericArg<'tcx>; 8]>, defs: &ty::Generics, mk_kind: &mut F) - where F: FnMut(&ty::GenericParamDef, &[Kind<'tcx>]) -> Kind<'tcx> + where F: FnMut(&ty::GenericParamDef, &[GenericArg<'tcx>]) -> GenericArg<'tcx> { substs.reserve(defs.params.len()); for param in &defs.params { @@ -287,7 +304,7 @@ #[inline] pub fn types(&'a self) -> impl DoubleEndedIterator<Item = Ty<'tcx>> + 'a { self.iter().filter_map(|k| { - if let UnpackedKind::Type(ty) = k.unpack() { + if let GenericArgKind::Type(ty) = k.unpack() { Some(ty) } else { None @@ -298,7 +315,7 @@ #[inline] pub fn regions(&'a self) -> impl DoubleEndedIterator<Item = ty::Region<'tcx>> + 'a { self.iter().filter_map(|k| { - if let UnpackedKind::Lifetime(lt) = k.unpack() { + if let GenericArgKind::Lifetime(lt) = k.unpack() { Some(lt) } else { None @@ -309,7 +326,7 @@ #[inline] pub fn consts(&'a self) -> impl DoubleEndedIterator<Item = &'tcx ty::Const<'tcx>> + 'a { self.iter().filter_map(|k| { - if let UnpackedKind::Const(ct) = k.unpack() { + if let GenericArgKind::Const(ct) = k.unpack() { Some(ct) } else { None @@ -320,10 +337,10 @@ #[inline] pub fn non_erasable_generics( &'a self - ) -> impl DoubleEndedIterator<Item = UnpackedKind<'tcx>> + 'a { + ) -> impl DoubleEndedIterator<Item = GenericArgKind<'tcx>> + 'a { self.iter().filter_map(|k| { match k.unpack() { - UnpackedKind::Lifetime(_) => None, + GenericArgKind::Lifetime(_) => None, generic => Some(generic), } }) @@ -331,7 +348,7 @@ #[inline] pub fn type_at(&self, i: usize) -> Ty<'tcx> { - if let UnpackedKind::Type(ty) = self[i].unpack() { + if let GenericArgKind::Type(ty) = self[i].unpack() { ty } else { bug!("expected type for param #{} in {:?}", i, self); @@ -340,7 +357,7 @@ #[inline] pub fn region_at(&self, i: usize) -> ty::Region<'tcx> { - if let UnpackedKind::Lifetime(lt) = self[i].unpack() { + if let GenericArgKind::Lifetime(lt) = self[i].unpack() { lt } else { bug!("expected region for param #{} in {:?}", i, self); @@ -349,7 +366,7 @@ #[inline] pub fn const_at(&self, i: usize) -> &'tcx ty::Const<'tcx> { - if let UnpackedKind::Const(ct) = self[i].unpack() { + if let GenericArgKind::Const(ct) = self[i].unpack() { ct } else { bug!("expected const for param #{} in {:?}", i, self); @@ -357,7 +374,7 @@ } #[inline] - pub fn type_for_def(&self, def: &ty::GenericParamDef) -> Kind<'tcx> { + pub fn type_for_def(&self, def: &ty::GenericParamDef) -> GenericArg<'tcx> { self.type_at(def.index as usize).into() } @@ -383,14 +400,41 @@ impl<'tcx> TypeFoldable<'tcx> for SubstsRef<'tcx> { fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { - let params: SmallVec<[_; 8]> = self.iter().map(|k| k.fold_with(folder)).collect(); - - // If folding doesn't change the substs, it's faster to avoid - // calling `mk_substs` and instead reuse the existing substs. - if params[..] == self[..] { - self - } else { - folder.tcx().intern_substs(¶ms) + // This code is hot enough that it's worth specializing for the most + // common length lists, to avoid the overhead of `SmallVec` creation. + // The match arms are in order of frequency. The 1, 2, and 0 cases are + // typically hit in 90--99.99% of cases. When folding doesn't change + // the substs, it's faster to reuse the existing substs rather than + // calling `intern_substs`. + match self.len() { + 1 => { + let param0 = self[0].fold_with(folder); + if param0 == self[0] { + self + } else { + folder.tcx().intern_substs(&[param0]) + } + } + 2 => { + let param0 = self[0].fold_with(folder); + let param1 = self[1].fold_with(folder); + if param0 == self[0] && param1 == self[1] { + self + } else { + folder.tcx().intern_substs(&[param0, param1]) + } + } + 0 => { + self + } + _ => { + let params: SmallVec<[_; 8]> = self.iter().map(|k| k.fold_with(folder)).collect(); + if params[..] == self[..] { + self + } else { + folder.tcx().intern_substs(¶ms) + } + } } } @@ -409,15 +453,25 @@ // there is more information available (for better errors). pub trait Subst<'tcx>: Sized { - fn subst(&self, tcx: TyCtxt<'tcx>, substs: &[Kind<'tcx>]) -> Self { + fn subst(&self, tcx: TyCtxt<'tcx>, substs: &[GenericArg<'tcx>]) -> Self { self.subst_spanned(tcx, substs, None) } - fn subst_spanned(&self, tcx: TyCtxt<'tcx>, substs: &[Kind<'tcx>], span: Option<Span>) -> Self; + fn subst_spanned( + &self, + tcx: TyCtxt<'tcx>, + substs: &[GenericArg<'tcx>], + span: Option<Span>, + ) -> Self; } impl<'tcx, T: TypeFoldable<'tcx>> Subst<'tcx> for T { - fn subst_spanned(&self, tcx: TyCtxt<'tcx>, substs: &[Kind<'tcx>], span: Option<Span>) -> T { + fn subst_spanned( + &self, + tcx: TyCtxt<'tcx>, + substs: &[GenericArg<'tcx>], + span: Option<Span>, + ) -> T { let mut folder = SubstFolder { tcx, substs, span, @@ -433,7 +487,7 @@ struct SubstFolder<'a, 'tcx> { tcx: TyCtxt<'tcx>, - substs: &'a [Kind<'tcx>], + substs: &'a [GenericArg<'tcx>], /// The location for which the substitution is performed, if available. span: Option<Span>, @@ -468,7 +522,7 @@ ty::ReEarlyBound(data) => { let rk = self.substs.get(data.index as usize).map(|k| k.unpack()); match rk { - Some(UnpackedKind::Lifetime(lt)) => { + Some(GenericArgKind::Lifetime(lt)) => { self.shift_region_through_binders(lt) } _ => { @@ -501,7 +555,7 @@ } self.ty_stack_depth += 1; - let t1 = match t.sty { + let t1 = match t.kind { ty::Param(p) => { self.ty_for_param(p, t) } @@ -537,7 +591,7 @@ // Look up the type in the substitutions. It really should be in there. let opt_ty = self.substs.get(p.index as usize).map(|k| k.unpack()); let ty = match opt_ty { - Some(UnpackedKind::Type(ty)) => ty, + Some(GenericArgKind::Type(ty)) => ty, Some(kind) => { let span = self.span.unwrap_or(DUMMY_SP); span_bug!( @@ -578,7 +632,7 @@ // Look up the const in the substitutions. It really should be in there. let opt_ct = self.substs.get(p.index as usize).map(|k| k.unpack()); let ct = match opt_ct { - Some(UnpackedKind::Const(ct)) => ct, + Some(GenericArgKind::Const(ct)) => ct, Some(kind) => { let span = self.span.unwrap_or(DUMMY_SP); span_bug!(
diff --git a/src/librustc/ty/trait_def.rs b/src/librustc/ty/trait_def.rs index 2bb9c25..49ec908 100644 --- a/src/librustc/ty/trait_def.rs +++ b/src/librustc/ty/trait_def.rs
@@ -8,8 +8,7 @@ use crate::ty::{Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, - StableHasherResult}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::HashStable; /// A trait's definition with type information. @@ -194,9 +193,7 @@ } impl<'a> HashStable<StableHashingContext<'a>> for TraitImpls { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let TraitImpls { ref blanket_impls, ref non_blanket_impls,
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 2bb009d..5555dac 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs
@@ -8,7 +8,7 @@ use crate::ich::NodeIdHashingMode; use crate::traits::{self, ObligationCause}; use crate::ty::{self, DefIdTree, Ty, TyCtxt, GenericParamDefKind, TypeFoldable}; -use crate::ty::subst::{Subst, InternalSubsts, SubstsRef, UnpackedKind}; +use crate::ty::subst::{Subst, InternalSubsts, SubstsRef, GenericArgKind}; use crate::ty::query::TyCtxtAt; use crate::ty::TyKind::*; use crate::ty::layout::{Integer, IntegerExt}; @@ -33,7 +33,7 @@ impl<'tcx> fmt::Display for Discr<'tcx> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.ty.sty { + match self.ty.kind { ty::Int(ity) => { let size = ty::tls::with(|tcx| { Integer::from_attr(&tcx, SignedInt(ity)).size() @@ -54,7 +54,7 @@ self.checked_add(tcx, 1).0 } pub fn checked_add(self, tcx: TyCtxt<'tcx>, n: u128) -> (Self, bool) { - let (int, signed) = match self.ty.sty { + let (int, signed) = match self.ty.kind { Int(ity) => (Integer::from_attr(&tcx, SignedInt(ity)), true), Uint(uty) => (Integer::from_attr(&tcx, UnsignedInt(uty)), false), _ => bug!("non integer discriminant"), @@ -179,7 +179,7 @@ ) -> Result<(), CopyImplementationError<'tcx>> { // FIXME: (@jroesch) float this code up tcx.infer_ctxt().enter(|infcx| { - let (adt, substs) = match self_type.sty { + let (adt, substs) = match self_type.kind { // These types used to have a builtin impl. // Now libcore provides that impl. ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Float(_) | @@ -246,10 +246,10 @@ impl<'tcx> TyCtxt<'tcx> { pub fn has_error_field(self, ty: Ty<'tcx>) -> bool { - if let ty::Adt(def, substs) = ty.sty { + if let ty::Adt(def, substs) = ty.kind { for field in def.all_fields() { let field_ty = field.ty(self, substs); - if let Error = field_ty.sty { + if let Error = field_ty.kind { return true; } } @@ -298,7 +298,7 @@ -> Ty<'tcx> { loop { - match ty.sty { + match ty.kind { ty::Adt(def, substs) => { if !def.is_struct() { break; @@ -370,7 +370,7 @@ { let (mut a, mut b) = (source, target); loop { - match (&a.sty, &b.sty) { + match (&a.kind, &b.kind) { (&Adt(a_def, a_substs), &Adt(b_def, b_substs)) if a_def == b_def && a_def.is_struct() => { if let Some(f) = a_def.non_enum_variant().fields.last() { @@ -510,7 +510,7 @@ /// destructor of `def` itself. For the destructors of the /// contents, you need `adt_dtorck_constraint`. pub fn destructor_constraints(self, def: &'tcx ty::AdtDef) - -> Vec<ty::subst::Kind<'tcx>> + -> Vec<ty::subst::GenericArg<'tcx>> { let dtor = match def.destructor(self) { None => { @@ -544,12 +544,12 @@ // <P1, P2, P0>, and then look up which of the impl substs refer to // parameters marked as pure. - let impl_substs = match self.type_of(impl_def_id).sty { + let impl_substs = match self.type_of(impl_def_id).kind { ty::Adt(def_, substs) if def_ == def => substs, _ => bug!() }; - let item_substs = match self.type_of(def.did).sty { + let item_substs = match self.type_of(def.did).kind { ty::Adt(def_, substs) if def_ == def => substs, _ => bug!() }; @@ -557,23 +557,23 @@ let result = item_substs.iter().zip(impl_substs.iter()) .filter(|&(_, &k)| { match k.unpack() { - UnpackedKind::Lifetime(&ty::RegionKind::ReEarlyBound(ref ebr)) => { + GenericArgKind::Lifetime(&ty::RegionKind::ReEarlyBound(ref ebr)) => { !impl_generics.region_param(ebr, self).pure_wrt_drop } - UnpackedKind::Type(&ty::TyS { - sty: ty::Param(ref pt), .. + GenericArgKind::Type(&ty::TyS { + kind: ty::Param(ref pt), .. }) => { !impl_generics.type_param(pt, self).pure_wrt_drop } - UnpackedKind::Const(&ty::Const { + GenericArgKind::Const(&ty::Const { val: ConstValue::Param(ref pc), .. }) => { !impl_generics.const_param(pc, self).pure_wrt_drop } - UnpackedKind::Lifetime(_) | - UnpackedKind::Type(_) | - UnpackedKind::Const(_) => { + GenericArgKind::Lifetime(_) | + GenericArgKind::Type(_) | + GenericArgKind::Const(_) => { // Not a type, const or region param: this should be reported // as an error. false @@ -642,12 +642,12 @@ /// wrapped in a binder. pub fn closure_env_ty(self, closure_def_id: DefId, - closure_substs: ty::ClosureSubsts<'tcx>) + closure_substs: SubstsRef<'tcx>) -> Option<ty::Binder<Ty<'tcx>>> { let closure_ty = self.mk_closure(closure_def_id, closure_substs); let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); - let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self); + let closure_kind_ty = closure_substs.as_closure().kind_ty(closure_def_id, self); let closure_kind = closure_kind_ty.to_opt_closure_kind()?; let env_ty = match closure_kind { ty::ClosureKind::Fn => self.mk_imm_ref(self.mk_region(env_region), closure_ty), @@ -743,7 +743,7 @@ } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Opaque(def_id, substs) = t.sty { + if let ty::Opaque(def_id, substs) = t.kind { self.expand_opaque_ty(def_id, substs).unwrap_or(t) } else if t.has_projections() { t.super_fold_with(self) @@ -818,13 +818,15 @@ /// /// (Note that this implies that if `ty` has a destructor attached, /// then `needs_drop` will definitely return `true` for `ty`.) + /// + /// Note that this method is used to check eligible types in unions. #[inline] pub fn needs_drop(&'tcx self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { tcx.needs_drop_raw(param_env.and(self)).0 } pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.sty, &b.sty) { + match (&a.kind, &b.kind) { (&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => { if did_a != did_b { return false; @@ -859,7 +861,7 @@ representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>, ty: Ty<'tcx>, ) -> Representability { - match ty.sty { + match ty.kind { Tuple(..) => { // Find non representable fold_repr(ty.tuple_fields().map(|ty| { @@ -902,7 +904,7 @@ } fn same_struct_or_enum<'tcx>(ty: Ty<'tcx>, def: &'tcx ty::AdtDef) -> bool { - match ty.sty { + match ty.kind { Adt(ty_def, _) => { ty_def == def } @@ -940,7 +942,7 @@ representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>, ty: Ty<'tcx>, ) -> Representability { - match ty.sty { + match ty.kind { Adt(def, _) => { { // Iterate through stack of previously seen types. @@ -1022,7 +1024,7 @@ /// - `&'a *const &'b u8 -> *const &'b u8` pub fn peel_refs(&'tcx self) -> Ty<'tcx> { let mut ty = self; - while let Ref(_, inner_ty, _) = ty.sty { + while let Ref(_, inner_ty, _) = ty.kind { ty = inner_ty; } ty @@ -1030,34 +1032,25 @@ } fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - let (param_env, ty) = query.into_parts(); - let trait_def_id = tcx.require_lang_item(lang_items::CopyTraitLangItem, None); - tcx.infer_ctxt() - .enter(|infcx| traits::type_known_to_meet_bound_modulo_regions( - &infcx, - param_env, - ty, - trait_def_id, - DUMMY_SP, - )) + is_item_raw(tcx, query, lang_items::CopyTraitLangItem) } fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - let (param_env, ty) = query.into_parts(); - let trait_def_id = tcx.require_lang_item(lang_items::SizedTraitLangItem, None); - tcx.infer_ctxt() - .enter(|infcx| traits::type_known_to_meet_bound_modulo_regions( - &infcx, - param_env, - ty, - trait_def_id, - DUMMY_SP, - )) + is_item_raw(tcx, query, lang_items::SizedTraitLangItem) + } fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + is_item_raw(tcx, query, lang_items::FreezeTraitLangItem) +} + +fn is_item_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, + item: lang_items::LangItem, +) -> bool { let (param_env, ty) = query.into_parts(); - let trait_def_id = tcx.require_lang_item(lang_items::FreezeTraitLangItem, None); + let trait_def_id = tcx.require_lang_item(item, None); tcx.infer_ctxt() .enter(|infcx| traits::type_known_to_meet_bound_modulo_regions( &infcx, @@ -1080,7 +1073,7 @@ assert!(!ty.needs_infer()); - NeedsDrop(match ty.sty { + NeedsDrop(match ty.kind { // Fast-path for primitive types ty::Infer(ty::FreshIntTy(_)) | ty::Infer(ty::FreshFloatTy(_)) | ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Never | @@ -1118,10 +1111,15 @@ ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), + // Zero-length arrays never contain anything to drop. + ty::Array(_, len) if len.try_eval_usize(tcx, param_env) == Some(0) => false, + // Structural recursion. ty::Array(ty, _) | ty::Slice(ty) => needs_drop(ty), - ty::Closure(def_id, ref substs) => substs.upvar_tys(def_id, tcx).any(needs_drop), + ty::Closure(def_id, ref substs) => { + substs.as_closure().upvar_tys(def_id, tcx).any(needs_drop) + } // Pessimistically assume that all generators will require destructors // as we don't know if a destructor is a noop or not until after the MIR @@ -1183,7 +1181,7 @@ { use self::ExplicitSelf::*; - match self_arg_ty.sty { + match self_arg_ty.kind { _ if is_self_ty(self_arg_ty) => ByValue, ty::Ref(region, ty, mutbl) if is_self_ty(ty) => { ByReference(region, mutbl)
diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs index 8c31107..f5b1902 100644 --- a/src/librustc/ty/walk.rs +++ b/src/librustc/ty/walk.rs
@@ -69,7 +69,7 @@ // natural order one would expect (basically, the order of the // types as they are written). fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) { - match parent_ty.sty { + match parent_ty.kind { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Infer(_) | ty::Param(_) | ty::Never | ty::Error | ty::Placeholder(..) | ty::Bound(..) | ty::Foreign(..) => { @@ -110,11 +110,9 @@ ty::Adt(_, substs) | ty::Opaque(_, substs) => { stack.extend(substs.types().rev()); } - ty::Closure(_, ref substs) => { - stack.extend(substs.substs.types().rev()); - } - ty::Generator(_, ref substs, _) => { - stack.extend(substs.substs.types().rev()); + ty::Closure(_, ref substs) + | ty::Generator(_, ref substs, _) => { + stack.extend(substs.types().rev()); } ty::GeneratorWitness(ts) => { stack.extend(ts.skip_binder().iter().cloned().rev());
diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index d6de217..4ea01bf 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs
@@ -22,11 +22,14 @@ ty: Ty<'tcx>, span: Span, ) -> Option<Vec<traits::PredicateObligation<'tcx>>> { - let mut wf = WfPredicates { infcx, - param_env, - body_id, - span, - out: vec![] }; + let mut wf = WfPredicates { + infcx, + param_env, + body_id, + span, + out: vec![], + item: None, + }; if wf.compute(ty) { debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out); let result = wf.normalize(); @@ -47,8 +50,9 @@ body_id: hir::HirId, trait_ref: &ty::TraitRef<'tcx>, span: Span, + item: Option<&'tcx hir::Item>, ) -> Vec<traits::PredicateObligation<'tcx>> { - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![] }; + let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item }; wf.compute_trait_ref(trait_ref, Elaborate::All); wf.normalize() } @@ -60,7 +64,7 @@ predicate: &ty::Predicate<'tcx>, span: Span, ) -> Vec<traits::PredicateObligation<'tcx>> { - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![] }; + let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; // (*) ok to skip binders, because wf code is prepared for it match *predicate { @@ -107,6 +111,7 @@ body_id: hir::HirId, span: Span, out: Vec<traits::PredicateObligation<'tcx>>, + item: Option<&'tcx hir::Item>, } /// Controls whether we "elaborate" supertraits and so forth on the WF @@ -157,33 +162,162 @@ .collect() } - /// Pushes the obligations required for `trait_ref` to be WF into - /// `self.out`. + /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) { + let tcx = self.infcx.tcx; let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs); let cause = self.cause(traits::MiscObligation); let param_env = self.param_env; + let item = &self.item; + let extend_cause_with_original_assoc_item_obligation = | + cause: &mut traits::ObligationCause<'_>, + pred: &ty::Predicate<'_>, + trait_assoc_items: ty::AssocItemsIterator<'_>, + | { + let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span)); + match pred { + ty::Predicate::Projection(proj) => { + // The obligation comes not from the current `impl` nor the `trait` being + // implemented, but rather from a "second order" obligation, like in + // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs`: + // + // error[E0271]: type mismatch resolving `<Foo2 as Bar2>::Ok == ()` + // --> $DIR/point-at-type-on-obligation-failure.rs:13:5 + // | + // LL | type Ok; + // | -- associated type defined here + // ... + // LL | impl Bar for Foo { + // | ---------------- in this `impl` item + // LL | type Ok = (); + // | ^^^^^^^^^^^^^ expected u32, found () + // | + // = note: expected type `u32` + // found type `()` + // + // FIXME: we would want to point a span to all places that contributed to this + // obligation. In the case above, it should be closer to: + // + // error[E0271]: type mismatch resolving `<Foo2 as Bar2>::Ok == ()` + // --> $DIR/point-at-type-on-obligation-failure.rs:13:5 + // | + // LL | type Ok; + // | -- associated type defined here + // LL | type Sibling: Bar2<Ok=Self::Ok>; + // | -------------------------------- obligation set here + // ... + // LL | impl Bar for Foo { + // | ---------------- in this `impl` item + // LL | type Ok = (); + // | ^^^^^^^^^^^^^ expected u32, found () + // ... + // LL | impl Bar2 for Foo2 { + // | ---------------- in this `impl` item + // LL | type Ok = u32; + // | -------------- obligation set here + // | + // = note: expected type `u32` + // found type `()` + if let Some(hir::ItemKind::Impl(.., impl_items)) = item.map(|i| &i.kind) { + let trait_assoc_item = tcx.associated_item(proj.projection_def_id()); + if let Some(impl_item) = impl_items.iter().filter(|item| { + item.ident == trait_assoc_item.ident + }).next() { + cause.span = impl_item.span; + cause.code = traits::AssocTypeBound( + item_span, + trait_assoc_item.ident.span, + ); + } + } + } + ty::Predicate::Trait(proj) => { + // An associated item obligation born out of the `trait` failed to be met. + // Point at the `impl` that failed the obligation, the associated item that + // needed to meet the obligation, and the definition of that associated item, + // which should hold the obligation in most cases. An example can be seen in + // `src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs`: + // + // error[E0277]: the trait bound `bool: Bar` is not satisfied + // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 + // | + // LL | type Assoc: Bar; + // | ----- associated type defined here + // ... + // LL | impl Foo for () { + // | --------------- in this `impl` item + // LL | type Assoc = bool; + // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` + // + // FIXME: if the obligation comes from the where clause in the `trait`, we + // should point at it: + // + // error[E0277]: the trait bound `bool: Bar` is not satisfied + // --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5 + // | + // | trait Foo where <Self as Foo>>::Assoc: Bar { + // | -------------------------- obligation set here + // LL | type Assoc; + // | ----- associated type defined here + // ... + // LL | impl Foo for () { + // | --------------- in this `impl` item + // LL | type Assoc = bool; + // | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool` + if let ( + ty::Projection(ty::ProjectionTy { item_def_id, .. }), + Some(hir::ItemKind::Impl(.., impl_items)), + ) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind)) { + if let Some((impl_item, trait_assoc_item)) = trait_assoc_items + .filter(|i| i.def_id == *item_def_id) + .next() + .and_then(|trait_assoc_item| impl_items.iter() + .filter(|i| i.ident == trait_assoc_item.ident) + .next() + .map(|impl_item| (impl_item, trait_assoc_item))) + { + cause.span = impl_item.span; + cause.code = traits::AssocTypeBound( + item_span, + trait_assoc_item.ident.span, + ); + } + } + } + _ => {} + } + }; + if let Elaborate::All = elaborate { + let trait_assoc_items = tcx.associated_items(trait_ref.def_id); + let predicates = obligations.iter() - .map(|obligation| obligation.predicate.clone()) - .collect(); - let implied_obligations = traits::elaborate_predicates(self.infcx.tcx, predicates); + .map(|obligation| obligation.predicate.clone()) + .collect(); + let implied_obligations = traits::elaborate_predicates(tcx, predicates); let implied_obligations = implied_obligations.map(|pred| { - traits::Obligation::new(cause.clone(), param_env, pred) + let mut cause = cause.clone(); + extend_cause_with_original_assoc_item_obligation( + &mut cause, + &pred, + trait_assoc_items.clone(), + ); + traits::Obligation::new(cause, param_env, pred) }); self.out.extend(implied_obligations); } self.out.extend(obligations); - self.out.extend( - trait_ref.substs.types() - .filter(|ty| !ty.has_escaping_bound_vars()) - .map(|ty| traits::Obligation::new(cause.clone(), - param_env, - ty::Predicate::WellFormed(ty)))); + self.out.extend(trait_ref.substs.types() + .filter(|ty| !ty.has_escaping_bound_vars()) + .map(|ty| traits::Obligation::new( + cause.clone(), + param_env, + ty::Predicate::WellFormed(ty), + ))); } /// Pushes the obligations required for `trait_ref::Item` to be WF @@ -236,7 +370,7 @@ let mut subtys = ty0.walk(); let param_env = self.param_env; while let Some(ty) = subtys.next() { - match ty.sty { + match ty.kind { ty::Bool | ty::Char | ty::Int(..) | @@ -347,7 +481,7 @@ // anyway, except via auto trait matching (which // only inspects the upvar types). subtys.skip_current_subtree(); // subtree handled by compute_projection - for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) { + for upvar_ty in substs.as_closure().upvar_tys(def_id, self.infcx.tcx) { self.compute(upvar_ty); } } @@ -380,16 +514,21 @@ // obligations that don't refer to Self and // checking those - let cause = self.cause(traits::MiscObligation); - let component_traits = - data.auto_traits().chain(data.principal_def_id()); - self.out.extend( - component_traits.map(|did| traits::Obligation::new( - cause.clone(), - param_env, - ty::Predicate::ObjectSafe(did) - )) - ); + let defer_to_coercion = + self.infcx.tcx.features().object_safe_for_dispatch; + + if !defer_to_coercion { + let cause = self.cause(traits::MiscObligation); + let component_traits = + data.auto_traits().chain(data.principal_def_id()); + self.out.extend( + component_traits.map(|did| traits::Obligation::new( + cause.clone(), + param_env, + ty::Predicate::ObjectSafe(did) + )) + ); + } } // Inference variables are the complicated case, since we don't @@ -407,7 +546,7 @@ // is satisfied to ensure termination.) ty::Infer(_) => { let ty = self.infcx.shallow_resolve(ty); - if let ty::Infer(_) = ty.sty { // not yet resolved... + if let ty::Infer(_) = ty.kind { // not yet resolved... if ty == ty0 { // ...this is the type we started from! no progress. return false; }
diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index 2475b93..3e52a6a 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs
@@ -1,17 +1,13 @@ #![allow(non_camel_case_types)] -use rustc_data_structures::{fx::FxHashMap, sync::Lock}; +use rustc_data_structures::sync::Lock; -use std::cell::{RefCell, Cell}; +use std::cell::Cell; use std::fmt::Debug; -use std::hash::Hash; use std::time::{Duration, Instant}; -use std::sync::mpsc::{Sender}; -use syntax_pos::{SpanData}; use syntax::symbol::{Symbol, sym}; use rustc_macros::HashStable; -use crate::dep_graph::{DepNode}; use crate::session::Session; #[cfg(test)] @@ -27,17 +23,6 @@ thread_local!(static TIME_DEPTH: Cell<usize> = Cell::new(0)); -/// Parameters to the `Dump` variant of type `ProfileQueriesMsg`. -#[derive(Clone,Debug)] -pub struct ProfQDumpParams { - /// A base path for the files we will dump. - pub path:String, - /// To ensure that the compiler waits for us to finish our dumps. - pub ack:Sender<()>, - /// Toggle dumping a log file with every `ProfileQueriesMsg`. - pub dump_profq_msg_log:bool, -} - #[allow(nonstandard_style)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct QueryMsg { @@ -45,53 +30,6 @@ pub msg: Option<String>, } -/// A sequence of these messages induce a trace of query-based incremental compilation. -// FIXME(matthewhammer): Determine whether we should include cycle detection here or not. -#[derive(Clone,Debug)] -pub enum ProfileQueriesMsg { - /// Begin a timed pass. - TimeBegin(String), - /// End a timed pass. - TimeEnd, - /// Begin a task (see `dep_graph::graph::with_task`). - TaskBegin(DepNode), - /// End a task. - TaskEnd, - /// Begin a new query. - /// Cannot use `Span` because queries are sent to other thread. - QueryBegin(SpanData, QueryMsg), - /// Query is satisfied by using an already-known value for the given key. - CacheHit, - /// Query requires running a provider; providers may nest, permitting queries to nest. - ProviderBegin, - /// Query is satisfied by a provider terminating with a value. - ProviderEnd, - /// Dump a record of the queries to the given path. - Dump(ProfQDumpParams), - /// Halt the profiling/monitoring background thread. - Halt -} - -/// If enabled, send a message to the profile-queries thread. -pub fn profq_msg(sess: &Session, msg: ProfileQueriesMsg) { - if let Some(s) = sess.profile_channel.borrow().as_ref() { - s.send(msg).unwrap() - } else { - // Do nothing. - } -} - -/// Set channel for profile queries channel. -pub fn profq_set_chan(sess: &Session, s: Sender<ProfileQueriesMsg>) -> bool { - let mut channel = sess.profile_channel.borrow_mut(); - if channel.is_none() { - *channel = Some(s); - true - } else { - false - } -} - /// Read the current depth of `time()` calls. This is used to /// encourage indentation across threads. pub fn time_depth() -> usize { @@ -108,10 +46,10 @@ pub fn time<T, F>(sess: &Session, what: &str, f: F) -> T where F: FnOnce() -> T, { - time_ext(sess.time_passes(), Some(sess), what, f) + time_ext(sess.time_passes(), what, f) } -pub fn time_ext<T, F>(do_it: bool, sess: Option<&Session>, what: &str, f: F) -> T where +pub fn time_ext<T, F>(do_it: bool, what: &str, f: F) -> T where F: FnOnce() -> T, { if !do_it { return f(); } @@ -122,19 +60,9 @@ r }); - if let Some(sess) = sess { - if cfg!(debug_assertions) { - profq_msg(sess, ProfileQueriesMsg::TimeBegin(what.to_string())) - } - } let start = Instant::now(); let rv = f(); let dur = start.elapsed(); - if let Some(sess) = sess { - if cfg!(debug_assertions) { - profq_msg(sess, ProfileQueriesMsg::TimeEnd) - } - } print_time_passes_entry(true, what, dur); @@ -279,39 +207,3 @@ debug!(">>"); Indenter { _cannot_construct_outside_of_this_module: () } } - -pub trait MemoizationMap { - type Key: Clone; - type Value: Clone; - - /// If `key` is present in the map, return the value, - /// otherwise invoke `op` and store the value in the map. - /// - /// N.B., if the receiver is a `DepTrackingMap`, special care is - /// needed in the `op` to ensure that the correct edges are - /// added into the dep graph. See the `DepTrackingMap` impl for - /// more details! - fn memoize<OP>(&self, key: Self::Key, op: OP) -> Self::Value - where OP: FnOnce() -> Self::Value; -} - -impl<K, V> MemoizationMap for RefCell<FxHashMap<K,V>> - where K: Hash+Eq+Clone, V: Clone -{ - type Key = K; - type Value = V; - - fn memoize<OP>(&self, key: K, op: OP) -> V - where OP: FnOnce() -> V - { - let result = self.borrow().get(&key).cloned(); - match result { - Some(result) => result, - None => { - let result = op(); - self.borrow_mut().insert(key, result.clone()); - result - } - } - } -}
diff --git a/src/librustc/util/profiling.rs b/src/librustc/util/profiling.rs index 8624856..5a1b7f3 100644 --- a/src/librustc/util/profiling.rs +++ b/src/librustc/util/profiling.rs
@@ -1,9 +1,9 @@ -use std::borrow::Cow; use std::error::Error; use std::fs; use std::mem::{self, Discriminant}; use std::path::Path; use std::process; +use std::sync::Arc; use std::thread::ThreadId; use std::u32; @@ -14,9 +14,12 @@ /// MmapSerializatioSink is faster on macOS and Linux /// but FileSerializationSink is faster on Windows #[cfg(not(windows))] -type Profiler = measureme::Profiler<measureme::MmapSerializationSink>; +type SerializationSink = measureme::MmapSerializationSink; #[cfg(windows)] -type Profiler = measureme::Profiler<measureme::FileSerializationSink>; +type SerializationSink = measureme::FileSerializationSink; + +type Profiler = measureme::Profiler<SerializationSink>; + #[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] pub enum ProfileCategory { @@ -62,6 +65,146 @@ unsafe { mem::transmute::<ThreadId, u64>(tid) } } + +/// A reference to the SelfProfiler. It can be cloned and sent across thread +/// boundaries at will. +#[derive(Clone)] +pub struct SelfProfilerRef { + // This field is `None` if self-profiling is disabled for the current + // compilation session. + profiler: Option<Arc<SelfProfiler>>, + + // We store the filter mask directly in the reference because that doesn't + // cost anything and allows for filtering with checking if the profiler is + // actually enabled. + event_filter_mask: EventFilter, +} + +impl SelfProfilerRef { + + pub fn new(profiler: Option<Arc<SelfProfiler>>) -> SelfProfilerRef { + // If there is no SelfProfiler then the filter mask is set to NONE, + // ensuring that nothing ever tries to actually access it. + let event_filter_mask = profiler + .as_ref() + .map(|p| p.event_filter_mask) + .unwrap_or(EventFilter::NONE); + + SelfProfilerRef { + profiler, + event_filter_mask, + } + } + + // This shim makes sure that calls only get executed if the filter mask + // lets them pass. It also contains some trickery to make sure that + // code is optimized for non-profiling compilation sessions, i.e. anything + // past the filter check is never inlined so it doesn't clutter the fast + // path. + #[inline(always)] + fn exec<F>(&self, event_filter: EventFilter, f: F) -> TimingGuard<'_> + where F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a> + { + #[inline(never)] + fn cold_call<F>(profiler_ref: &SelfProfilerRef, f: F) -> TimingGuard<'_> + where F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a> + { + let profiler = profiler_ref.profiler.as_ref().unwrap(); + f(&**profiler) + } + + if unlikely!(self.event_filter_mask.contains(event_filter)) { + cold_call(self, f) + } else { + TimingGuard::none() + } + } + + /// Start profiling a generic activity. Profiling continues until the + /// TimingGuard returned from this call is dropped. + #[inline(always)] + pub fn generic_activity(&self, event_id: &str) -> TimingGuard<'_> { + self.exec(EventFilter::GENERIC_ACTIVITIES, |profiler| { + let event_id = profiler.profiler.alloc_string(event_id); + TimingGuard::start( + profiler, + profiler.generic_activity_event_kind, + event_id + ) + }) + } + + /// Start profiling a query provider. Profiling continues until the + /// TimingGuard returned from this call is dropped. + #[inline(always)] + pub fn query_provider(&self, query_name: QueryName) -> TimingGuard<'_> { + self.exec(EventFilter::QUERY_PROVIDERS, |profiler| { + let event_id = SelfProfiler::get_query_name_string_id(query_name); + TimingGuard::start(profiler, profiler.query_event_kind, event_id) + }) + } + + /// Record a query in-memory cache hit. + #[inline(always)] + pub fn query_cache_hit(&self, query_name: QueryName) { + self.non_guard_query_event( + |profiler| profiler.query_cache_hit_event_kind, + query_name, + EventFilter::QUERY_CACHE_HITS, + TimestampKind::Instant, + ); + } + + /// Start profiling a query being blocked on a concurrent execution. + /// Profiling continues until the TimingGuard returned from this call is + /// dropped. + #[inline(always)] + pub fn query_blocked(&self, query_name: QueryName) -> TimingGuard<'_> { + self.exec(EventFilter::QUERY_BLOCKED, |profiler| { + let event_id = SelfProfiler::get_query_name_string_id(query_name); + TimingGuard::start(profiler, profiler.query_blocked_event_kind, event_id) + }) + } + + /// Start profiling how long it takes to load a query result from the + /// incremental compilation on-disk cache. Profiling continues until the + /// TimingGuard returned from this call is dropped. + #[inline(always)] + pub fn incr_cache_loading(&self, query_name: QueryName) -> TimingGuard<'_> { + self.exec(EventFilter::INCR_CACHE_LOADS, |profiler| { + let event_id = SelfProfiler::get_query_name_string_id(query_name); + TimingGuard::start( + profiler, + profiler.incremental_load_result_event_kind, + event_id + ) + }) + } + + #[inline(always)] + fn non_guard_query_event( + &self, + event_kind: fn(&SelfProfiler) -> StringId, + query_name: QueryName, + event_filter: EventFilter, + timestamp_kind: TimestampKind + ) { + drop(self.exec(event_filter, |profiler| { + let event_id = SelfProfiler::get_query_name_string_id(query_name); + let thread_id = thread_id_to_u64(std::thread::current().id()); + + profiler.profiler.record_event( + event_kind(profiler), + event_id, + thread_id, + timestamp_kind, + ); + + TimingGuard::none() + })); + } +} + pub struct SelfProfiler { profiler: Profiler, event_filter_mask: EventFilter, @@ -143,103 +286,28 @@ let id = SelfProfiler::get_query_name_string_id(query_name); self.profiler.alloc_string_with_reserved_id(id, query_name.as_str()); } +} +#[must_use] +pub struct TimingGuard<'a>(Option<measureme::TimingGuard<'a, SerializationSink>>); + +impl<'a> TimingGuard<'a> { #[inline] - pub fn start_activity( - &self, - label: impl Into<Cow<'static, str>>, - ) { - if self.event_filter_mask.contains(EventFilter::GENERIC_ACTIVITIES) { - self.record(&label.into(), self.generic_activity_event_kind, TimestampKind::Start); - } - } - - #[inline] - pub fn end_activity( - &self, - label: impl Into<Cow<'static, str>>, - ) { - if self.event_filter_mask.contains(EventFilter::GENERIC_ACTIVITIES) { - self.record(&label.into(), self.generic_activity_event_kind, TimestampKind::End); - } - } - - #[inline] - pub fn record_query_hit(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::QUERY_CACHE_HITS) { - self.record_query(query_name, self.query_cache_hit_event_kind, TimestampKind::Instant); - } - } - - #[inline] - pub fn start_query(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::QUERY_PROVIDERS) { - self.record_query(query_name, self.query_event_kind, TimestampKind::Start); - } - } - - #[inline] - pub fn end_query(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::QUERY_PROVIDERS) { - self.record_query(query_name, self.query_event_kind, TimestampKind::End); - } - } - - #[inline] - pub fn incremental_load_result_start(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::INCR_CACHE_LOADS) { - self.record_query( - query_name, - self.incremental_load_result_event_kind, - TimestampKind::Start - ); - } - } - - #[inline] - pub fn incremental_load_result_end(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::INCR_CACHE_LOADS) { - self.record_query( - query_name, - self.incremental_load_result_event_kind, - TimestampKind::End - ); - } - } - - #[inline] - pub fn query_blocked_start(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::QUERY_BLOCKED) { - self.record_query(query_name, self.query_blocked_event_kind, TimestampKind::Start); - } - } - - #[inline] - pub fn query_blocked_end(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::QUERY_BLOCKED) { - self.record_query(query_name, self.query_blocked_event_kind, TimestampKind::End); - } - } - - #[inline] - fn record(&self, event_id: &str, event_kind: StringId, timestamp_kind: TimestampKind) { - let thread_id = thread_id_to_u64(std::thread::current().id()); - - let event_id = self.profiler.alloc_string(event_id); - self.profiler.record_event(event_kind, event_id, thread_id, timestamp_kind); - } - - #[inline] - fn record_query( - &self, - query_name: QueryName, + pub fn start( + profiler: &'a SelfProfiler, event_kind: StringId, - timestamp_kind: TimestampKind, - ) { - let dep_node_name = SelfProfiler::get_query_name_string_id(query_name); - + event_id: StringId, + ) -> TimingGuard<'a> { let thread_id = thread_id_to_u64(std::thread::current().id()); + let raw_profiler = &profiler.profiler; + let timing_guard = raw_profiler.start_recording_interval_event(event_kind, + event_id, + thread_id); + TimingGuard(Some(timing_guard)) + } - self.profiler.record_event(event_kind, dep_node_name, thread_id, timestamp_kind); + #[inline] + pub fn none() -> TimingGuard<'a> { + TimingGuard(None) } }
diff --git a/src/librustc_apfloat/Cargo.toml b/src/librustc_apfloat/Cargo.toml index af6c2fe..4fc15f9 100644 --- a/src/librustc_apfloat/Cargo.toml +++ b/src/librustc_apfloat/Cargo.toml
@@ -9,5 +9,5 @@ path = "lib.rs" [dependencies] -bitflags = "1.0" +bitflags = "1.2.1" smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
diff --git a/src/librustc_apfloat/ieee.rs b/src/librustc_apfloat/ieee.rs index 18d968f..4abb86a 100644 --- a/src/librustc_apfloat/ieee.rs +++ b/src/librustc_apfloat/ieee.rs
@@ -1199,8 +1199,8 @@ } // Handle a leading minus sign. - let minus = s.starts_with("-"); - if minus || s.starts_with("+") { + let minus = s.starts_with('-'); + if minus || s.starts_with('+') { s = &s[1..]; if s.is_empty() { return Err(ParseError("String has no digits"));
diff --git a/src/librustc_apfloat/lib.rs b/src/librustc_apfloat/lib.rs index 1190cea..5efe4fd 100644 --- a/src/librustc_apfloat/lib.rs +++ b/src/librustc_apfloat/lib.rs
@@ -555,12 +555,13 @@ fn ilogb(self) -> ExpInt; /// Returns: self * 2<sup>exp</sup> for integral exponents. + /// Equivalent to C standard library function `ldexp`. fn scalbn_r(self, exp: ExpInt, round: Round) -> Self; fn scalbn(self, exp: ExpInt) -> Self { self.scalbn_r(exp, Round::NearestTiesToEven) } - /// Equivalent of C standard library function. + /// Equivalent to C standard library function with the same name. /// /// While the C standard says exp is an unspecified value for infinity and nan, /// this returns INT_MAX for infinities, and INT_MIN for NaNs (see `ilogb`).
diff --git a/src/librustc_ast_borrowck/Cargo.toml b/src/librustc_ast_borrowck/Cargo.toml deleted file mode 100644 index 024b264..0000000 --- a/src/librustc_ast_borrowck/Cargo.toml +++ /dev/null
@@ -1,22 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_ast_borrowck" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_ast_borrowck" -path = "lib.rs" -test = false -doctest = false - -[dependencies] -log = "0.4" -syntax = { path = "../libsyntax" } -syntax_pos = { path = "../libsyntax_pos" } -# for "clarity", rename the graphviz crate to dot; graphviz within `borrowck` -# refers to the borrowck-specific graphviz adapter traits. -dot = { path = "../libgraphviz", package = "graphviz" } -rustc = { path = "../librustc" } -errors = { path = "../librustc_errors", package = "rustc_errors" } -rustc_data_structures = { path = "../librustc_data_structures" }
diff --git a/src/librustc_ast_borrowck/borrowck/README.md b/src/librustc_ast_borrowck/borrowck/README.md deleted file mode 100644 index 3f21759..0000000 --- a/src/librustc_ast_borrowck/borrowck/README.md +++ /dev/null
@@ -1,1167 +0,0 @@ -% The Borrow Checker - -> WARNING: This README is more or less obsolete, and will be removed -> soon! The new system is described in the [rustc guide]. - -[rustc guide]: https://rust-lang.github.io/rustc-guide/borrow_check.html - -This pass has the job of enforcing memory safety. This is a subtle -topic. This docs aim to explain both the practice and the theory -behind the borrow checker. They start with a high-level overview of -how it works, and then proceed to dive into the theoretical -background. Finally, they go into detail on some of the more subtle -aspects. - -# Table of contents - -These docs are long. Search for the section you are interested in. - -- Overview -- Formal model -- Borrowing and loans -- Moves and initialization -- Drop flags and structural fragments -- Future work - -# Overview - -The borrow checker checks one function at a time. It operates in two -passes. The first pass, called `gather_loans`, walks over the function -and identifies all of the places where borrows (e.g., `&` expressions -and `ref` bindings) and moves (copies or captures of a linear value) -occur. It also tracks initialization sites. For each borrow and move, -it checks various basic safety conditions at this time (for example, -that the lifetime of the borrow doesn't exceed the lifetime of the -value being borrowed, or that there is no move out of an `&T` -referent). - -It then uses the dataflow module to propagate which of those borrows -may be in scope at each point in the procedure. A loan is considered -to come into scope at the expression that caused it and to go out of -scope when the lifetime of the resulting reference expires. - -Once the in-scope loans are known for each point in the program, the -borrow checker walks the IR again in a second pass called -`check_loans`. This pass examines each statement and makes sure that -it is safe with respect to the in-scope loans. - -# Formal model - -Throughout the docs we'll consider a simple subset of Rust in which -you can only borrow from places, defined like so: - -```text -P = x | P.f | *P -``` - -Here `x` represents some variable, `P.f` is a field reference, -and `*P` is a pointer dereference. There is no auto-deref or other -niceties. This means that if you have a type like: - -```rust -struct S { f: i32 } -``` - -and a variable `a: Box<S>`, then the rust expression `a.f` would correspond -to an `P` of `(*a).f`. - -Here is the formal grammar for the types we'll consider: - -```text -TY = i32 | bool | S<'LT...> | Box<TY> | & 'LT MQ TY -MQ = mut | imm -``` - -Most of these types should be pretty self explanatory. Here `S` is a -struct name and we assume structs are declared like so: - -```text -SD = struct S<'LT...> { (f: TY)... } -``` - -# Borrowing and loans - -## An intuitive explanation - -### Issuing loans - -Now, imagine we had a program like this: - -```rust -struct Foo { f: i32, g: i32 } -... -'a: { - let mut x: Box<Foo> = ...; - let y = &mut (*x).f; - x = ...; -} -``` - -This is of course dangerous because mutating `x` will free the old -value and hence invalidate `y`. The borrow checker aims to prevent -this sort of thing. - -#### Loans and restrictions - -The way the borrow checker works is that it analyzes each borrow -expression (in our simple model, that's stuff like `&P`, though in -real life there are a few other cases to consider). For each borrow -expression, it computes a `Loan`, which is a data structure that -records (1) the value being borrowed, (2) the mutability and scope of -the borrow, and (3) a set of restrictions. In the code, `Loan` is a -struct defined in `middle::borrowck`. Formally, we define `LOAN` as -follows: - -```text -LOAN = (P, LT, MQ, RESTRICTION*) -RESTRICTION = (P, ACTION*) -ACTION = MUTATE | CLAIM | FREEZE -``` - -Here the `LOAN` tuple defines the place `P` being borrowed; the -lifetime `LT` of that borrow; the mutability `MQ` of the borrow; and a -list of restrictions. The restrictions indicate actions which, if -taken, could invalidate the loan and lead to type safety violations. - -Each `RESTRICTION` is a pair of a restrictive place `P` (which will -either be the path that was borrowed or some prefix of the path that -was borrowed) and a set of restricted actions. There are three kinds -of actions that may be restricted for the path `P`: - -- `MUTATE` means that `P` cannot be assigned to; -- `CLAIM` means that the `P` cannot be borrowed mutably; -- `FREEZE` means that the `P` cannot be borrowed immutably; - -Finally, it is never possible to move from a place that appears in a -restriction. This implies that the "empty restriction" `(P, [])`, -which contains an empty set of actions, still has a purpose---it -prevents moves from `P`. I chose not to make `MOVE` a fourth kind of -action because that would imply that sometimes moves are permitted -from restricted values, which is not the case. - -#### Example - -To give you a better feeling for what kind of restrictions derived -from a loan, let's look at the loan `L` that would be issued as a -result of the borrow `&mut (*x).f` in the example above: - -```text -L = ((*x).f, 'a, mut, RS) where - RS = [((*x).f, [MUTATE, CLAIM, FREEZE]), - (*x, [MUTATE, CLAIM, FREEZE]), - (x, [MUTATE, CLAIM, FREEZE])] -``` - -The loan states that the expression `(*x).f` has been loaned as -mutable for the lifetime `'a`. Because the loan is mutable, that means -that the value `(*x).f` may be mutated via the newly created reference -(and *only* via that pointer). This is reflected in the -restrictions `RS` that accompany the loan. - -The first restriction `((*x).f, [MUTATE, CLAIM, FREEZE])` states that -the lender may not mutate, freeze, nor alias `(*x).f`. Mutation is -illegal because `(*x).f` is only supposed to be mutated via the new -reference, not by mutating the original path `(*x).f`. Freezing is -illegal because the path now has an `&mut` alias; so even if we the -lender were to consider `(*x).f` to be immutable, it might be mutated -via this alias. They will be enforced for the lifetime `'a` of the -loan. After the loan expires, the restrictions no longer apply. - -The second restriction on `*x` is interesting because it does not -apply to the path that was lent (`(*x).f`) but rather to a prefix of -the borrowed path. This is due to the rules of inherited mutability: -if the user were to assign to (or freeze) `*x`, they would indirectly -overwrite (or freeze) `(*x).f`, and thus invalidate the reference -that was created. In general it holds that when a path is -lent, restrictions are issued for all the owning prefixes of that -path. In this case, the path `*x` owns the path `(*x).f` and, -because `x` has ownership, the path `x` owns the path `*x`. -Therefore, borrowing `(*x).f` yields restrictions on both -`*x` and `x`. - -### Checking for illegal assignments, moves, and reborrows - -Once we have computed the loans introduced by each borrow, the borrow -checker uses a data flow propagation to compute the full set of loans -in scope at each expression and then uses that set to decide whether -that expression is legal. Remember that the scope of loan is defined -by its lifetime LT. We sometimes say that a loan which is in-scope at -a particular point is an "outstanding loan", and the set of -restrictions included in those loans as the "outstanding -restrictions". - -The kinds of expressions which in-scope loans can render illegal are: -- *assignments* (`lv = v`): illegal if there is an in-scope restriction - against mutating `lv`; -- *moves*: illegal if there is any in-scope restriction on `lv` at all; -- *mutable borrows* (`&mut lv`): illegal there is an in-scope restriction - against claiming `lv`; -- *immutable borrows* (`&lv`): illegal there is an in-scope restriction - against freezing `lv`. - -## Formal rules - -Now that we hopefully have some kind of intuitive feeling for how the -borrow checker works, let's look a bit more closely now at the precise -conditions that it uses. - -I will present the rules in a modified form of standard inference -rules, which looks as follows: - -```text -PREDICATE(X, Y, Z) // Rule-Name - Condition 1 - Condition 2 - Condition 3 -``` - -The initial line states the predicate that is to be satisfied. The -indented lines indicate the conditions that must be met for the -predicate to be satisfied. The right-justified comment states the name -of this rule: there are comments in the borrowck source referencing -these names, so that you can cross reference to find the actual code -that corresponds to the formal rule. - -### Invariants - -I want to collect, at a high-level, the invariants the borrow checker -maintains. I will give them names and refer to them throughout the -text. Together these invariants are crucial for the overall soundness -of the system. - -**Mutability requires uniqueness.** To mutate a path - -**Unique mutability.** There is only one *usable* mutable path to any -given memory at any given time. This implies that when claiming memory -with an expression like `p = &mut x`, the compiler must guarantee that -the borrowed value `x` can no longer be mutated so long as `p` is -live. (This is done via restrictions, read on.) - -**.** - - -### The `gather_loans` pass - -We start with the `gather_loans` pass, which walks the AST looking for -borrows. For each borrow, there are three bits of information: the -place `P` being borrowed and the mutability `MQ` and lifetime `LT` -of the resulting pointer. Given those, `gather_loans` applies four -validity tests: - -1. `MUTABILITY(P, MQ)`: The mutability of the reference is -compatible with the mutability of `P` (i.e., not borrowing immutable -data as mutable). - -2. `ALIASABLE(P, MQ)`: The aliasability of the reference is -compatible with the aliasability of `P`. The goal is to prevent -`&mut` borrows of aliasability data. - -3. `LIFETIME(P, LT, MQ)`: The lifetime of the borrow does not exceed -the lifetime of the value being borrowed. - -4. `RESTRICTIONS(P, LT, ACTIONS) = RS`: This pass checks and computes the -restrictions to maintain memory safety. These are the restrictions -that will go into the final loan. We'll discuss in more detail below. - -## Checking mutability - -Checking mutability is fairly straightforward. We just want to prevent -immutable data from being borrowed as mutable. Note that it is ok to borrow -mutable data as immutable, since that is simply a freeze. The judgement -`MUTABILITY(P, MQ)` means the mutability of `P` is compatible with a borrow -of mutability `MQ`. The Rust code corresponding to this predicate is the -function `check_mutability` in `middle::borrowck::gather_loans`. - -### Checking mutability of variables - -*Code pointer:* Function `check_mutability()` in `gather_loans/mod.rs`, -but also the code in `mem_categorization`. - -Let's begin with the rules for variables, which state that if a -variable is declared as mutable, it may be borrowed any which way, but -otherwise the variable must be borrowed as immutable: - -```text -MUTABILITY(X, MQ) // M-Var-Mut - DECL(X) = mut - -MUTABILITY(X, imm) // M-Var-Imm - DECL(X) = imm -``` - -### Checking mutability of owned content - -Fields and boxes inherit their mutability from -their base expressions, so both of their rules basically -delegate the check to the base expression `P`: - -```text -MUTABILITY(P.f, MQ) // M-Field - MUTABILITY(P, MQ) - -MUTABILITY(*P, MQ) // M-Deref-Unique - TYPE(P) = Box<Ty> - MUTABILITY(P, MQ) -``` - -### Checking mutability of immutable pointer types - -Immutable pointer types like `&T` can only -be borrowed if MQ is immutable: - -```text -MUTABILITY(*P, imm) // M-Deref-Borrowed-Imm - TYPE(P) = &Ty -``` - -### Checking mutability of mutable pointer types - -`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut: - -```text -MUTABILITY(*P, MQ) // M-Deref-Borrowed-Mut - TYPE(P) = &mut Ty -``` - -## Checking aliasability - -The goal of the aliasability check is to ensure that we never permit `&mut` -borrows of aliasable data. The judgement `ALIASABLE(P, MQ)` means the -aliasability of `P` is compatible with a borrow of mutability `MQ`. The Rust -code corresponding to this predicate is the function `check_aliasability()` in -`middle::borrowck::gather_loans`. - -### Checking aliasability of variables - -Local variables are never aliasable as they are accessible only within -the stack frame. - -```text - ALIASABLE(X, MQ) // M-Var-Mut -``` - -### Checking aliasable of owned content - -Owned content is aliasable if it is found in an aliasable location: - -```text -ALIASABLE(P.f, MQ) // M-Field - ALIASABLE(P, MQ) - -ALIASABLE(*P, MQ) // M-Deref-Unique - ALIASABLE(P, MQ) -``` - -### Checking aliasability of immutable pointer types - -Immutable pointer types like `&T` are aliasable, and hence can only be -borrowed immutably: - -```text -ALIASABLE(*P, imm) // M-Deref-Borrowed-Imm - TYPE(P) = &Ty -``` - -### Checking aliasability of mutable pointer types - -`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut: - -```text -ALIASABLE(*P, MQ) // M-Deref-Borrowed-Mut - TYPE(P) = &mut Ty -``` - -## Checking lifetime - -These rules aim to ensure that no data is borrowed for a scope that exceeds -its lifetime. These two computations wind up being intimately related. -Formally, we define a predicate `LIFETIME(P, LT, MQ)`, which states that -"the place `P` can be safely borrowed for the lifetime `LT` with mutability -`MQ`". The Rust code corresponding to this predicate is the module -`middle::borrowck::gather_loans::lifetime`. - -### Checking lifetime of variables - -The rule for variables states that a variable can only be borrowed a -lifetime `LT` that is a subregion of the variable's scope: - -```text -LIFETIME(X, LT, MQ) // L-Local - LT <= block where X is declared -``` - -### Checking lifetime for owned content - -The lifetime of a field or box is the same as the lifetime -of its owner: - -```text -LIFETIME(P.f, LT, MQ) // L-Field - LIFETIME(P, LT, MQ) - -LIFETIME(*P, LT, MQ) // L-Deref-Send - TYPE(P) = Box<Ty> - LIFETIME(P, LT, MQ) -``` - -### Checking lifetime for derefs of references - -References have a lifetime `LT'` associated with them. The -data they point at has been guaranteed to be valid for at least this -lifetime. Therefore, the borrow is valid so long as the lifetime `LT` -of the borrow is shorter than the lifetime `LT'` of the pointer -itself: - -```text -LIFETIME(*P, LT, MQ) // L-Deref-Borrowed - TYPE(P) = <' Ty OR <' mut Ty - LT <= LT' -``` - -## Computing the restrictions - -The final rules govern the computation of *restrictions*, meaning that -we compute the set of actions that will be illegal for the life of the -loan. The predicate is written `RESTRICTIONS(P, LT, ACTIONS) = -RESTRICTION*`, which can be read "in order to prevent `ACTIONS` from -occurring on `P`, the restrictions `RESTRICTION*` must be respected -for the lifetime of the loan". - -Note that there is an initial set of restrictions: these restrictions -are computed based on the kind of borrow: - -```text -&mut P => RESTRICTIONS(P, LT, MUTATE|CLAIM|FREEZE) -&P => RESTRICTIONS(P, LT, MUTATE|CLAIM) -``` - -The reasoning here is that a mutable borrow must be the only writer, -therefore it prevents other writes (`MUTATE`), mutable borrows -(`CLAIM`), and immutable borrows (`FREEZE`). An immutable borrow -permits other immutable borrows but forbids writes and mutable borrows. - -### Restrictions for loans of a local variable - -The simplest case is a borrow of a local variable `X`: - -```text -RESTRICTIONS(X, LT, ACTIONS) = (X, ACTIONS) // R-Variable -``` - -In such cases we just record the actions that are not permitted. - -### Restrictions for loans of fields - -Restricting a field is the same as restricting the owner of that -field: - -```text -RESTRICTIONS(P.f, LT, ACTIONS) = RS, (P.f, ACTIONS) // R-Field - RESTRICTIONS(P, LT, ACTIONS) = RS -``` - -The reasoning here is as follows. If the field must not be mutated, -then you must not mutate the owner of the field either, since that -would indirectly modify the field. Similarly, if the field cannot be -frozen or aliased, we cannot allow the owner to be frozen or aliased, -since doing so indirectly freezes/aliases the field. This is the -origin of inherited mutability. - -### Restrictions for loans of owned referents - -Because the mutability of owned referents is inherited, restricting an -owned referent is similar to restricting a field, in that it implies -restrictions on the pointer. However, boxes have an important -twist: if the owner `P` is mutated, that causes the owned referent -`*P` to be freed! So whenever an owned referent `*P` is borrowed, we -must prevent the box `P` from being mutated, which means -that we always add `MUTATE` and `CLAIM` to the restriction set imposed -on `P`: - -```text -RESTRICTIONS(*P, LT, ACTIONS) = RS, (*P, ACTIONS) // R-Deref-Send-Pointer - TYPE(P) = Box<Ty> - RESTRICTIONS(P, LT, ACTIONS|MUTATE|CLAIM) = RS -``` - -### Restrictions for loans of immutable borrowed referents - -Immutable borrowed referents are freely aliasable, meaning that -the compiler does not prevent you from copying the pointer. This -implies that issuing restrictions is useless. We might prevent the -user from acting on `*P` itself, but there could be another path -`*P1` that refers to the exact same memory, and we would not be -restricting that path. Therefore, the rule for `&Ty` pointers -always returns an empty set of restrictions, and it only permits -restricting `MUTATE` and `CLAIM` actions: - -```text -RESTRICTIONS(*P, LT, ACTIONS) = [] // R-Deref-Imm-Borrowed - TYPE(P) = <' Ty - LT <= LT' // (1) - ACTIONS subset of [MUTATE, CLAIM] -``` - -The reason that we can restrict `MUTATE` and `CLAIM` actions even -without a restrictions list is that it is never legal to mutate nor to -borrow mutably the contents of a `&Ty` pointer. In other words, -those restrictions are already inherent in the type. - -Clause (1) in the rule for `&Ty` deserves mention. Here I -specify that the lifetime of the loan must be less than the lifetime -of the `&Ty` pointer. In simple cases, this clause is redundant, since -the `LIFETIME()` function will already enforce the required rule: - -```rust -fn foo(point: &'a Point) -> &'static i32 { - &point.x // Error -} -``` - -The above example fails to compile both because of clause (1) above -but also by the basic `LIFETIME()` check. However, in more advanced -examples involving multiple nested pointers, clause (1) is needed: - -```rust -fn foo(point: &'a &'b mut Point) -> &'b i32 { - &point.x // Error -} -``` - -The `LIFETIME` rule here would accept `'b` because, in fact, the -*memory is* guaranteed to remain valid (i.e., not be freed) for the -lifetime `'b`, since the `&mut` pointer is valid for `'b`. However, we -are returning an immutable reference, so we need the memory to be both -valid and immutable. Even though `point.x` is referenced by an `&mut` -pointer, it can still be considered immutable so long as that `&mut` -pointer is found in an aliased location. That means the memory is -guaranteed to be *immutable* for the lifetime of the `&` pointer, -which is only `'a`, not `'b`. Hence this example yields an error. - -As a final twist, consider the case of two nested *immutable* -pointers, rather than a mutable pointer within an immutable one: - -```rust -fn foo(point: &'a &'b Point) -> &'b i32 { - &point.x // OK -} -``` - -This function is legal. The reason for this is that the inner pointer -(`*point : &'b Point`) is enough to guarantee the memory is immutable -and valid for the lifetime `'b`. This is reflected in -`RESTRICTIONS()` by the fact that we do not recurse (i.e., we impose -no restrictions on `P`, which in this particular case is the pointer -`point : &'a &'b Point`). - -#### Why both `LIFETIME()` and `RESTRICTIONS()`? - -Given the previous text, it might seem that `LIFETIME` and -`RESTRICTIONS` should be folded together into one check, but there is -a reason that they are separated. They answer separate concerns. -The rules pertaining to `LIFETIME` exist to ensure that we don't -create a borrowed pointer that outlives the memory it points at. So -`LIFETIME` prevents a function like this: - -```rust -fn get_1<'a>() -> &'a i32 { - let x = 1; - &x -} -``` - -Here we would be returning a pointer into the stack. Clearly bad. - -However, the `RESTRICTIONS` rules are more concerned with how memory -is used. The example above doesn't generate an error according to -`RESTRICTIONS` because, for local variables, we don't require that the -loan lifetime be a subset of the local variable lifetime. The idea -here is that we *can* guarantee that `x` is not (e.g.) mutated for the -lifetime `'a`, even though `'a` exceeds the function body and thus -involves unknown code in the caller -- after all, `x` ceases to exist -after we return and hence the remaining code in `'a` cannot possibly -mutate it. This distinction is important for type checking functions -like this one: - -```rust -fn inc_and_get<'a>(p: &'a mut Point) -> &'a i32 { - p.x += 1; - &p.x -} -``` - -In this case, we take in a `&mut` and return a frozen borrowed pointer -with the same lifetime. So long as the lifetime of the returned value -doesn't exceed the lifetime of the `&mut` we receive as input, this is -fine, though it may seem surprising at first (it surprised me when I -first worked it through). After all, we're guaranteeing that `*p` -won't be mutated for the lifetime `'a`, even though we can't "see" the -entirety of the code during that lifetime, since some of it occurs in -our caller. But we *do* know that nobody can mutate `*p` except -through `p`. So if we don't mutate `*p` and we don't return `p`, then -we know that the right to mutate `*p` has been lost to our caller -- -in terms of capability, the caller passed in the ability to mutate -`*p`, and we never gave it back. (Note that we can't return `p` while -`*p` is borrowed since that would be a move of `p`, as `&mut` pointers -are affine.) - -### Restrictions for loans of mutable borrowed referents - -Mutable borrowed pointers are guaranteed to be the only way to mutate -their referent. This permits us to take greater license with them; for -example, the referent can be frozen simply be ensuring that we do not -use the original pointer to perform mutate. Similarly, we can allow -the referent to be claimed, so long as the original pointer is unused -while the new claimant is live. - -The rule for mutable borrowed pointers is as follows: - -```text -RESTRICTIONS(*P, LT, ACTIONS) = RS, (*P, ACTIONS) // R-Deref-Mut-Borrowed - TYPE(P) = <' mut Ty - LT <= LT' // (1) - RESTRICTIONS(P, LT, ACTIONS) = RS // (2) -``` - -Let's examine the two numbered clauses: - -Clause (1) specifies that the lifetime of the loan (`LT`) cannot -exceed the lifetime of the `&mut` pointer (`LT'`). The reason for this -is that the `&mut` pointer is guaranteed to be the only legal way to -mutate its referent -- but only for the lifetime `LT'`. After that -lifetime, the loan on the referent expires and hence the data may be -modified by its owner again. This implies that we are only able to -guarantee that the referent will not be modified or aliased for a -maximum of `LT'`. - -Here is a concrete example of a bug this rule prevents: - -```rust -// Test region-reborrow-from-shorter-mut-ref.rs: -fn copy_borrowed_ptr<'a,'b,T>(x: &'a mut &'b mut T) -> &'b mut T { - &mut **p // ERROR due to clause (1) -} -fn main() { - let mut x = 1; - let mut y = &mut x; // <-'b-----------------------------+ - // +-'a--------------------+ | - // v v | - let z = copy_borrowed_ptr(&mut y); // y is lent | - *y += 1; // Here y==z, so both should not be usable... | - *z += 1; // ...and yet they would be, but for clause 1. | -} // <------------------------------------------------------+ -``` - -Clause (2) propagates the restrictions on the referent to the pointer -itself. This is the same as with an box, though the -reasoning is mildly different. The basic goal in all cases is to -prevent the user from establishing another route to the same data. To -see what I mean, let's examine various cases of what can go wrong and -show how it is prevented. - -**Example danger 1: Moving the base pointer.** One of the simplest -ways to violate the rules is to move the base pointer to a new name -and access it via that new name, thus bypassing the restrictions on -the old name. Here is an example: - -```rust -// src/test/compile-fail/borrowck-move-mut-base-ptr.rs -fn foo(t0: &mut i32) { - let p: &i32 = &*t0; // Freezes `*t0` - let t1 = t0; //~ ERROR cannot move out of `t0` - *t1 = 22; // OK, not a write through `*t0` -} -``` - -Remember that `&mut` pointers are linear, and hence `let t1 = t0` is a -move of `t0` -- or would be, if it were legal. Instead, we get an -error, because clause (2) imposes restrictions on `P` (`t0`, here), -and any restrictions on a path make it impossible to move from that -path. - -**Example danger 2: Claiming the base pointer.** Another possible -danger is to mutably borrow the base path. This can lead to two bad -scenarios. The most obvious is that the mutable borrow itself becomes -another path to access the same data, as shown here: - -```rust -// src/test/compile-fail/borrowck-mut-borrow-of-mut-base-ptr.rs -fn foo<'a>(mut t0: &'a mut i32, - mut t1: &'a mut i32) { - let p: &i32 = &*t0; // Freezes `*t0` - let mut t2 = &mut t0; //~ ERROR cannot borrow `t0` - **t2 += 1; // Mutates `*t0` -} -``` - -In this example, `**t2` is the same memory as `*t0`. Because `t2` is -an `&mut` pointer, `**t2` is a unique path and hence it would be -possible to mutate `**t2` even though that memory was supposed to be -frozen by the creation of `p`. However, an error is reported -- the -reason is that the freeze `&*t0` will restrict claims and mutation -against `*t0` which, by clause 2, in turn prevents claims and mutation -of `t0`. Hence the claim `&mut t0` is illegal. - -Another danger with an `&mut` pointer is that we could swap the `t0` -value away to create a new path: - -```rust -// src/test/compile-fail/borrowck-swap-mut-base-ptr.rs -fn foo<'a>(mut t0: &'a mut i32, - mut t1: &'a mut i32) { - let p: &i32 = &*t0; // Freezes `*t0` - swap(&mut t0, &mut t1); //~ ERROR cannot borrow `t0` - *t1 = 22; -} -``` - -This is illegal for the same reason as above. Note that if we added -back a swap operator -- as we used to have -- we would want to be very -careful to ensure this example is still illegal. - -**Example danger 3: Freeze the base pointer.** In the case where the -referent is claimed, even freezing the base pointer can be dangerous, -as shown in the following example: - -```rust -// src/test/compile-fail/borrowck-borrow-of-mut-base-ptr.rs -fn foo<'a>(mut t0: &'a mut i32, - mut t1: &'a mut i32) { - let p: &mut i32 = &mut *t0; // Claims `*t0` - let mut t2 = &t0; //~ ERROR cannot borrow `t0` - let q: &i32 = &*t2; // Freezes `*t0` but not through `*p` - *p += 1; // violates type of `*q` -} -``` - -Here the problem is that `*t0` is claimed by `p`, and hence `p` wants -to be the controlling pointer through which mutation or freezes occur. -But `t2` would -- if it were legal -- have the type `& &mut i32`, and -hence would be a mutable pointer in an aliasable location, which is -considered frozen (since no one can write to `**t2` as it is not a -unique path). Therefore, we could reasonably create a frozen `&i32` -pointer pointing at `*t0` that coexists with the mutable pointer `p`, -which is clearly unsound. - -However, it is not always unsafe to freeze the base pointer. In -particular, if the referent is frozen, there is no harm in it: - -```rust -// src/test/ui/borrowck-borrow-of-mut-base-ptr-safe.rs -fn foo<'a>(mut t0: &'a mut i32, - mut t1: &'a mut i32) { - let p: &i32 = &*t0; // Freezes `*t0` - let mut t2 = &t0; - let q: &i32 = &*t2; // Freezes `*t0`, but that's ok... - let r: &i32 = &*t0; // ...after all, could do same thing directly. -} -``` - -In this case, creating the alias `t2` of `t0` is safe because the only -thing `t2` can be used for is to further freeze `*t0`, which is -already frozen. In particular, we cannot assign to `*t0` through the -new alias `t2`, as demonstrated in this test case: - -```rust -// src/test/ui/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs -fn foo(t0: & &mut i32) { - let t1 = t0; - let p: &i32 = &**t0; - **t1 = 22; //~ ERROR cannot assign -} -``` - -This distinction is reflected in the rules. When doing an `&mut` -borrow -- as in the first example -- the set `ACTIONS` will be -`CLAIM|MUTATE|FREEZE`, because claiming the referent implies that it -cannot be claimed, mutated, or frozen by anyone else. These -restrictions are propagated back to the base path and hence the base -path is considered unfreezable. - -In contrast, when the referent is merely frozen -- as in the second -example -- the set `ACTIONS` will be `CLAIM|MUTATE`, because freezing -the referent implies that it cannot be claimed or mutated but permits -others to freeze. Hence when these restrictions are propagated back to -the base path, it will still be considered freezable. - - - -**FIXME [RFC 1751](https://github.com/rust-lang/rfcs/issues/1751) -Restrictions against mutating the base pointer.** -When an `&mut` pointer is frozen or claimed, we currently pass along the -restriction against MUTATE to the base pointer. I do not believe this -restriction is needed. It dates from the days when we had a way to -mutate that preserved the value being mutated (i.e., swap). Nowadays -the only form of mutation is assignment, which destroys the pointer -being mutated -- therefore, a mutation cannot create a new path to the -same data. Rather, it removes an existing path. This implies that not -only can we permit mutation, we can have mutation kill restrictions in -the dataflow sense. - -**WARNING:** We do not currently have `const` borrows in the -language. If they are added back in, we must ensure that they are -consistent with all of these examples. The crucial question will be -what sorts of actions are permitted with a `&const &mut` pointer. I -would suggest that an `&mut` referent found in an `&const` location be -prohibited from both freezes and claims. This would avoid the need to -prevent `const` borrows of the base pointer when the referent is -borrowed. - -[ Previous revisions of this document discussed `&const` in more detail. -See the revision history. ] - -# Moves and initialization - -The borrow checker is also in charge of ensuring that: - -- all memory which is accessed is initialized -- immutable local variables are assigned at most once. - -These are two separate dataflow analyses built on the same -framework. Let's look at checking that memory is initialized first; -the checking of immutable local variable assignments works in a very -similar way. - -To track the initialization of memory, we actually track all the -points in the program that *create uninitialized memory*, meaning -moves and the declaration of uninitialized variables. For each of -these points, we create a bit in the dataflow set. Assignments to a -variable `x` or path `a.b.c` kill the move/uninitialization bits for -those paths and any subpaths (e.g., `x`, `x.y`, `a.b.c`, `*a.b.c`). -Bits are unioned when two control-flow paths join. Thus, the -presence of a bit indicates that the move may have occurred without an -intervening assignment to the same memory. At each use of a variable, -we examine the bits in scope, and check that none of them are -moves/uninitializations of the variable that is being used. - -Let's look at a simple example: - -```rust -fn foo(a: Box<i32>) { - let b: Box<i32>; // Gen bit 0. - - if cond { // Bits: 0 - use(&*a); - b = a; // Gen bit 1, kill bit 0. - use(&*b); - } else { - // Bits: 0 - } - // Bits: 0,1 - use(&*a); // Error. - use(&*b); // Error. -} - -fn use(a: &i32) { } -``` - -In this example, the variable `b` is created uninitialized. In one -branch of an `if`, we then move the variable `a` into `b`. Once we -exit the `if`, therefore, it is an error to use `a` or `b` since both -are only conditionally initialized. I have annotated the dataflow -state using comments. There are two dataflow bits, with bit 0 -corresponding to the creation of `b` without an initializer, and bit 1 -corresponding to the move of `a`. The assignment `b = a` both -generates bit 1, because it is a move of `a`, and kills bit 0, because -`b` is now initialized. On the else branch, though, `b` is never -initialized, and so bit 0 remains untouched. When the two flows of -control join, we union the bits from both sides, resulting in both -bits 0 and 1 being set. Thus any attempt to use `a` uncovers the bit 1 -from the "then" branch, showing that `a` may be moved, and any attempt -to use `b` uncovers bit 0, from the "else" branch, showing that `b` -may not be initialized. - -## Initialization of immutable variables - -Initialization of immutable variables works in a very similar way, -except that: - -1. we generate bits for each assignment to a variable; -2. the bits are never killed except when the variable goes out of scope. - -Thus the presence of an assignment bit indicates that the assignment -may have occurred. Note that assignments are only killed when the -variable goes out of scope, as it is not relevant whether or not there -has been a move in the meantime. Using these bits, we can declare that -an assignment to an immutable variable is legal iff there is no other -assignment bit to that same variable in scope. - -## Why is the design made this way? - -It may seem surprising that we assign dataflow bits to *each move* -rather than *each path being moved*. This is somewhat less efficient, -since on each use, we must iterate through all moves and check whether -any of them correspond to the path in question. Similar concerns apply -to the analysis for double assignments to immutable variables. The -main reason to do it this way is that it allows us to print better -error messages, because when a use occurs, we can print out the -precise move that may be in scope, rather than simply having to say -"the variable may not be initialized". - -## Data structures used in the move analysis - -The move analysis maintains several data structures that enable it to -cross-reference moves and assignments to determine when they may be -moving/assigning the same memory. These are all collected into the -`MoveData` and `FlowedMoveData` structs. The former represents the set -of move paths, moves, and assignments, and the latter adds in the -results of a dataflow computation. - -### Move paths - -The `MovePath` tree tracks every path that is moved or assigned to. -These paths have the same form as the `LoanPath` data structure, which -in turn is the "real world version of the places `P` that we -introduced earlier. The difference between a `MovePath` and a `LoanPath` -is that move paths are: - -1. Canonicalized, so that we have exactly one copy of each, and - we can refer to move paths by index; -2. Cross-referenced with other paths into a tree, so that given a move - path we can efficiently find all parent move paths and all - extensions (e.g., given the `a.b` move path, we can easily find the - move path `a` and also the move paths `a.b.c`) -3. Cross-referenced with moves and assignments, so that we can - easily find all moves and assignments to a given path. - -The mechanism that we use is to create a `MovePath` record for each -move path. These are arranged in an array and are referenced using -`MovePathIndex` values, which are newtype'd indices. The `MovePath` -structs are arranged into a tree, representing using the standard -Knuth representation where each node has a child 'pointer' and a "next -sibling" 'pointer'. In addition, each `MovePath` has a parent -'pointer'. In this case, the 'pointers' are just `MovePathIndex` -values. - -In this way, if we want to find all base paths of a given move path, -we can just iterate up the parent pointers (see `each_base_path()` in -the `move_data` module). If we want to find all extensions, we can -iterate through the subtree (see `each_extending_path()`). - -### Moves and assignments - -There are structs to represent moves (`Move`) and assignments -(`Assignment`), and these are also placed into arrays and referenced -by index. All moves of a particular path are arranged into a linked -lists, beginning with `MovePath.first_move` and continuing through -`Move.next_move`. - -We distinguish between "var" assignments, which are assignments to a -variable like `x = foo`, and "path" assignments (`x.f = foo`). This -is because we need to assign dataflows to the former, but not the -latter, so as to check for double initialization of immutable -variables. - -### Gathering and checking moves - -Like loans, we distinguish two phases. The first, gathering, is where -we uncover all the moves and assignments. As with loans, we do some -basic sanity checking in this phase, so we'll report errors if you -attempt to move out of a borrowed pointer etc. Then we do the dataflow -(see `FlowedMoveData::new`). Finally, in the `check_loans.rs` code, we -walk back over, identify all uses, assignments, and captures, and -check that they are legal given the set of dataflow bits we have -computed for that program point. - -# Drop flags and structural fragments - -In addition to the job of enforcing memory safety, the borrow checker -code is also responsible for identifying the *structural fragments* of -data in the function, to support out-of-band dynamic drop flags -allocated on the stack. (For background, see [RFC PR #320].) - -[RFC PR #320]: https://github.com/rust-lang/rfcs/pull/320 - -Semantically, each piece of data that has a destructor may need a -boolean flag to indicate whether or not its destructor has been run -yet. However, in many cases there is no need to actually maintain such -a flag: It can be apparent from the code itself that a given path is -always initialized (or always deinitialized) when control reaches the -end of its owner's scope, and thus we can unconditionally emit (or -not) the destructor invocation for that path. - -A simple example of this is the following: - -```rust -struct D { p: i32 } -impl D { fn new(x: i32) -> D { ... } -impl Drop for D { ... } - -fn foo(a: D, b: D, t: || -> bool) { - let c: D; - let d: D; - if t() { c = b; } -} -``` - -At the end of the body of `foo`, the compiler knows that `a` is -initialized, introducing a drop obligation (deallocating the boxed -integer) for the end of `a`'s scope that is run unconditionally. -Likewise the compiler knows that `d` is not initialized, and thus it -leave out the drop code for `d`. - -The compiler cannot statically know the drop-state of `b` nor `c` at -the end of their scope, since that depends on the value of -`t`. Therefore, we need to insert boolean flags to track whether we -need to drop `b` and `c`. - -However, the matter is not as simple as just mapping local variables -to their corresponding drop flags when necessary. In particular, in -addition to being able to move data out of local variables, Rust -allows one to move values in and out of structured data. - -Consider the following: - -```rust -struct S { x: D, y: D, z: D } - -fn foo(a: S, mut b: S, t: || -> bool) { - let mut c: S; - let d: S; - let e: S = a.clone(); - if t() { - c = b; - b.x = e.y; - } - if t() { c.y = D::new(4); } -} -``` - -As before, the drop obligations of `a` and `d` can be statically -determined, and again the state of `b` and `c` depend on dynamic -state. But additionally, the dynamic drop obligations introduced by -`b` and `c` are not just per-local boolean flags. For example, if the -first call to `t` returns `false` and the second call `true`, then at -the end of their scope, `b` will be completely initialized, but only -`c.y` in `c` will be initialized. If both calls to `t` return `true`, -then at the end of their scope, `c` will be completely initialized, -but only `b.x` will be initialized in `b`, and only `e.x` and `e.z` -will be initialized in `e`. - -Note that we need to cover the `z` field in each case in some way, -since it may (or may not) need to be dropped, even though `z` is never -directly mentioned in the body of the `foo` function. We call a path -like `b.z` a *fragment sibling* of `b.x`, since the field `z` comes -from the same structure `S` that declared the field `x` in `b.x`. - -In general we need to maintain boolean flags that match the -`S`-structure of both `b` and `c`. In addition, we need to consult -such a flag when doing an assignment (such as `c.y = D::new(4);` -above), in order to know whether or not there is a previous value that -needs to be dropped before we do the assignment. - -So for any given function, we need to determine what flags are needed -to track its drop obligations. Our strategy for determining the set of -flags is to represent the fragmentation of the structure explicitly: -by starting initially from the paths that are explicitly mentioned in -moves and assignments (such as `b.x` and `c.y` above), and then -traversing the structure of the path's type to identify leftover -*unmoved fragments*: assigning into `c.y` means that `c.x` and `c.z` -are leftover unmoved fragments. Each fragment represents a drop -obligation that may need to be tracked. Paths that are only moved or -assigned in their entirety (like `a` and `d`) are treated as a single -drop obligation. - -The fragment construction process works by piggy-backing on the -existing `move_data` module. We already have callbacks that visit each -direct move and assignment; these form the basis for the sets of -moved_leaf_paths and assigned_leaf_paths. From these leaves, we can -walk up their parent chain to identify all of their parent paths. -We need to identify the parents because of cases like the following: - -```rust -struct Pair<X,Y>{ x: X, y: Y } -fn foo(dd_d_d: Pair<Pair<Pair<D, D>, D>, D>) { - other_function(dd_d_d.x.y); -} -``` - -In this code, the move of the path `dd_d.x.y` leaves behind not only -the fragment drop-obligation `dd_d.x.x` but also `dd_d.y` as well. - -Once we have identified the directly-referenced leaves and their -parents, we compute the left-over fragments, in the function -`fragments::add_fragment_siblings`. As of this writing this works by -looking at each directly-moved or assigned path P, and blindly -gathering all sibling fields of P (as well as siblings for the parents -of P, etc). After accumulating all such siblings, we filter out the -entries added as siblings of P that turned out to be -directly-referenced paths (or parents of directly referenced paths) -themselves, thus leaving the never-referenced "left-overs" as the only -thing left from the gathering step. - -## Array structural fragments - -A special case of the structural fragments discussed above are -the elements of an array that has been passed by value, such as -the following: - -```rust -fn foo(a: [D; 10], i: i32) -> D { - a[i] -} -``` - -The above code moves a single element out of the input array `a`. -The remainder of the array still needs to be dropped; i.e., it -is a structural fragment. Note that after performing such a move, -it is not legal to read from the array `a`. There are a number of -ways to deal with this, but the important thing to note is that -the semantics needs to distinguish in some manner between a -fragment that is the *entire* array versus a fragment that represents -all-but-one element of the array. A place where that distinction -would arise is the following: - -```rust -fn foo(a: [D; 10], b: [D; 10], i: i32, t: bool) -> D { - if t { - a[i] - } else { - b[i] - } - - // When control exits, we will need either to drop all of `a` - // and all-but-one of `b`, or to drop all of `b` and all-but-one - // of `a`. -} -``` - -There are a number of ways that the codegen backend could choose to -compile this (e.g. a `[bool; 10]` array for each such moved array; -or an `Option<usize>` for each moved array). From the viewpoint of the -borrow-checker, the important thing is to record what kind of fragment -is implied by the relevant moves. - -# Future work - -While writing up these docs, I encountered some rules I believe to be -stricter than necessary: - -- I think restricting the `&mut` P against moves and `ALIAS` is sufficient, - `MUTATE` and `CLAIM` are overkill. `MUTATE` was necessary when swap was - a built-in operator, but as it is not, it is implied by `CLAIM`, - and `CLAIM` is implied by `ALIAS`. The only net effect of this is an - extra error message in some cases, though. -- I have not described how closures interact. Current code is unsound. - I am working on describing and implementing the fix. -- If we wish, we can easily extend the move checking to allow finer-grained - tracking of what is initialized and what is not, enabling code like - this: - - a = x.f.g; // x.f.g is now uninitialized - // here, x and x.f are not usable, but x.f.h *is* - x.f.g = b; // x.f.g is not initialized - // now x, x.f, x.f.g, x.f.h are all usable - - What needs to change here, most likely, is that the `moves` module - should record not only what paths are moved, but what expressions - are actual *uses*. For example, the reference to `x` in `x.f.g = b` - is not a true *use* in the sense that it requires `x` to be fully - initialized. This is in fact why the above code produces an error - today: the reference to `x` in `x.f.g = b` is considered illegal - because `x` is not fully initialized. - -There are also some possible refactorings: - -- It might be nice to replace all loan paths with the MovePath mechanism, - since they allow lightweight comparison using an integer.
diff --git a/src/librustc_ast_borrowck/borrowck/check_loans.rs b/src/librustc_ast_borrowck/borrowck/check_loans.rs deleted file mode 100644 index 3d824ee..0000000 --- a/src/librustc_ast_borrowck/borrowck/check_loans.rs +++ /dev/null
@@ -1,680 +0,0 @@ -// ---------------------------------------------------------------------- -// Checking loans -// -// Phase 2 of check: we walk down the tree and check that: -// 1. assignments are always made to mutable locations; -// 2. loans made in overlapping scopes do not conflict -// 3. assignments do not affect things loaned out as immutable -// 4. moves do not affect things loaned out in any way - -use crate::borrowck::*; -use crate::borrowck::InteriorKind::{InteriorElement, InteriorField}; -use rustc::middle::expr_use_visitor as euv; -use rustc::middle::expr_use_visitor::MutateMode; -use rustc::middle::mem_categorization as mc; -use rustc::middle::mem_categorization::Categorization; -use rustc::middle::region; -use rustc::ty::{self, TyCtxt, RegionKind}; -use syntax_pos::Span; -use rustc::hir; -use rustc::hir::Node; -use log::debug; - -use std::rc::Rc; - -// FIXME (#16118): These functions are intended to allow the borrow checker to -// be less precise in its handling of Box while still allowing moves out of a -// Box. They should be removed when Unique is removed from LoanPath. - -fn owned_ptr_base_path<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> &'a LoanPath<'tcx> { - //! Returns the base of the leftmost dereference of an Unique in - //! `loan_path`. If there is no dereference of an Unique in `loan_path`, - //! then it just returns `loan_path` itself. - - return match helper(loan_path) { - Some(new_loan_path) => new_loan_path, - None => loan_path, - }; - - fn helper<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> Option<&'a LoanPath<'tcx>> { - match loan_path.kind { - LpVar(_) | LpUpvar(_) => None, - LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => { - match helper(&lp_base) { - v @ Some(_) => v, - None => Some(&lp_base) - } - } - LpDowncast(ref lp_base, _) | - LpExtend(ref lp_base, ..) => helper(&lp_base) - } - } -} - -fn owned_ptr_base_path_rc<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Rc<LoanPath<'tcx>> { - //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than - //! a &LoanPath. - - return match helper(loan_path) { - Some(new_loan_path) => new_loan_path, - None => loan_path.clone() - }; - - fn helper<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Option<Rc<LoanPath<'tcx>>> { - match loan_path.kind { - LpVar(_) | LpUpvar(_) => None, - LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => { - match helper(lp_base) { - v @ Some(_) => v, - None => Some(lp_base.clone()) - } - } - LpDowncast(ref lp_base, _) | - LpExtend(ref lp_base, ..) => helper(lp_base) - } - } -} - -struct CheckLoanCtxt<'a, 'tcx> { - bccx: &'a BorrowckCtxt<'a, 'tcx>, - dfcx_loans: &'a LoanDataFlow<'tcx>, - move_data: &'a move_data::FlowedMoveData<'tcx>, - all_loans: &'a [Loan<'tcx>], - movable_generator: bool, -} - -impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> { - fn consume(&mut self, - consume_id: hir::HirId, - _: Span, - cmt: &mc::cmt_<'tcx>, - mode: euv::ConsumeMode) { - debug!("consume(consume_id={}, cmt={:?})", consume_id, cmt); - - self.consume_common(consume_id.local_id, cmt, mode); - } - - fn matched_pat(&mut self, - _matched_pat: &hir::Pat, - _cmt: &mc::cmt_<'_>, - _mode: euv::MatchMode) { } - - fn consume_pat(&mut self, - consume_pat: &hir::Pat, - cmt: &mc::cmt_<'tcx>, - mode: euv::ConsumeMode) { - debug!("consume_pat(consume_pat={:?}, cmt={:?})", consume_pat, cmt); - - self.consume_common(consume_pat.hir_id.local_id, cmt, mode); - } - - fn borrow(&mut self, - borrow_id: hir::HirId, - borrow_span: Span, - cmt: &mc::cmt_<'tcx>, - loan_region: ty::Region<'tcx>, - bk: ty::BorrowKind, - loan_cause: euv::LoanCause) - { - debug!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \ - bk={:?}, loan_cause={:?})", - borrow_id, cmt, loan_region, - bk, loan_cause); - - if let Some(lp) = opt_loan_path(cmt) { - self.check_if_path_is_moved(borrow_id.local_id, &lp); - } - - self.check_for_conflicting_loans(borrow_id.local_id); - - self.check_for_loans_across_yields(cmt, loan_region, borrow_span); - } - - fn mutate(&mut self, - assignment_id: hir::HirId, - _: Span, - assignee_cmt: &mc::cmt_<'tcx>, - mode: euv::MutateMode) - { - debug!("mutate(assignment_id={}, assignee_cmt={:?})", - assignment_id, assignee_cmt); - - if let Some(lp) = opt_loan_path(assignee_cmt) { - match mode { - MutateMode::Init | MutateMode::JustWrite => { - // In a case like `path = 1`, then path does not - // have to be *FULLY* initialized, but we still - // must be careful lest it contains derefs of - // pointers. - self.check_if_assigned_path_is_moved(assignee_cmt.hir_id.local_id, &lp); - } - MutateMode::WriteAndRead => { - // In a case like `path += 1`, then path must be - // fully initialized, since we will read it before - // we write it. - self.check_if_path_is_moved(assignee_cmt.hir_id.local_id, - &lp); - } - } - } - self.check_assignment(assignment_id.local_id, assignee_cmt); - } - - fn decl_without_init(&mut self, _id: hir::HirId, _span: Span) { } -} - -pub fn check_loans<'a, 'tcx>( - bccx: &BorrowckCtxt<'a, 'tcx>, - dfcx_loans: &LoanDataFlow<'tcx>, - move_data: &move_data::FlowedMoveData<'tcx>, - all_loans: &[Loan<'tcx>], - body: &hir::Body, -) { - debug!("check_loans(body id={})", body.value.hir_id); - - let def_id = bccx.tcx.hir().body_owner_def_id(body.id()); - - let hir_id = bccx.tcx.hir().as_local_hir_id(def_id).unwrap(); - let movable_generator = !match bccx.tcx.hir().get(hir_id) { - Node::Expr(&hir::Expr { - node: hir::ExprKind::Closure(.., Some(hir::GeneratorMovability::Static)), - .. - }) => true, - _ => false, - }; - - let param_env = bccx.tcx.param_env(def_id); - let mut clcx = CheckLoanCtxt { - bccx, - dfcx_loans, - move_data, - all_loans, - movable_generator, - }; - let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id); - euv::ExprUseVisitor::new(&mut clcx, - bccx.tcx, - def_id, - param_env, - &bccx.region_scope_tree, - bccx.tables, - Some(rvalue_promotable_map)) - .consume_body(body); -} - -fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind, - borrow_kind2: ty::BorrowKind) - -> bool { - borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow -} - -impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { - pub fn tcx(&self) -> TyCtxt<'tcx> { self.bccx.tcx } - - pub fn each_issued_loan<F>(&self, node: hir::ItemLocalId, mut op: F) -> bool where - F: FnMut(&Loan<'tcx>) -> bool, - { - //! Iterates over each loan that has been issued - //! on entrance to `node`, regardless of whether it is - //! actually *in scope* at that point. Sometimes loans - //! are issued for future scopes and thus they may have been - //! *issued* but not yet be in effect. - - self.dfcx_loans.each_bit_on_entry(node, |loan_index| { - let loan = &self.all_loans[loan_index]; - op(loan) - }) - } - - pub fn each_in_scope_loan<F>(&self, scope: region::Scope, mut op: F) -> bool where - F: FnMut(&Loan<'tcx>) -> bool, - { - //! Like `each_issued_loan()`, but only considers loans that are - //! currently in scope. - - self.each_issued_loan(scope.item_local_id(), |loan| { - if self.bccx.region_scope_tree.is_subscope_of(scope, loan.kill_scope) { - op(loan) - } else { - true - } - }) - } - - fn each_in_scope_loan_affecting_path<F>(&self, - scope: region::Scope, - loan_path: &LoanPath<'tcx>, - mut op: F) - -> bool where - F: FnMut(&Loan<'tcx>) -> bool, - { - //! Iterates through all of the in-scope loans affecting `loan_path`, - //! calling `op`, and ceasing iteration if `false` is returned. - - // First, we check for a loan restricting the path P being used. This - // accounts for borrows of P but also borrows of subpaths, like P.a.b. - // Consider the following example: - // - // let x = &mut a.b.c; // Restricts a, a.b, and a.b.c - // let y = a; // Conflicts with restriction - - let loan_path = owned_ptr_base_path(loan_path); - let cont = self.each_in_scope_loan(scope, |loan| { - let mut ret = true; - for restr_path in &loan.restricted_paths { - if **restr_path == *loan_path { - if !op(loan) { - ret = false; - break; - } - } - } - ret - }); - - if !cont { - return false; - } - - // Next, we must check for *loans* (not restrictions) on the path P or - // any base path. This rejects examples like the following: - // - // let x = &mut a.b; - // let y = a.b.c; - // - // Limiting this search to *loans* and not *restrictions* means that - // examples like the following continue to work: - // - // let x = &mut a.b; - // let y = a.c; - - let mut loan_path = loan_path; - loop { - match loan_path.kind { - LpVar(_) | LpUpvar(_) => { - break; - } - LpDowncast(ref lp_base, _) | - LpExtend(ref lp_base, ..) => { - loan_path = &lp_base; - } - } - - let cont = self.each_in_scope_loan(scope, |loan| { - if *loan.loan_path == *loan_path { - op(loan) - } else { - true - } - }); - - if !cont { - return false; - } - } - - return true; - } - - pub fn loans_generated_by(&self, node: hir::ItemLocalId) -> Vec<usize> { - //! Returns a vector of the loans that are generated as - //! we enter `node`. - - let mut result = Vec::new(); - self.dfcx_loans.each_gen_bit(node, |loan_index| { - result.push(loan_index); - true - }); - return result; - } - - pub fn check_for_loans_across_yields(&self, - cmt: &mc::cmt_<'tcx>, - loan_region: ty::Region<'tcx>, - borrow_span: Span) { - pub fn borrow_of_local_data(cmt: &mc::cmt_<'_>) -> bool { - match cmt.cat { - // Borrows of static items is allowed - Categorization::StaticItem => false, - // Reborrow of already borrowed data is ignored - // Any errors will be caught on the initial borrow - Categorization::Deref(..) => false, - - // By-ref upvars has Derefs so they will get ignored. - // Generators counts as FnOnce so this leaves only - // by-move upvars, which is local data for generators - Categorization::Upvar(..) => true, - - Categorization::ThreadLocal(region) | - Categorization::Rvalue(region) => { - // Rvalues promoted to 'static are no longer local - if let RegionKind::ReStatic = *region { - false - } else { - true - } - } - - // Borrow of local data must be checked - Categorization::Local(..) => true, - - // For interior references and downcasts, find out if the base is local - Categorization::Downcast(ref cmt_base, _) | - Categorization::Interior(ref cmt_base, _) => borrow_of_local_data(&cmt_base), - } - } - - if !self.movable_generator { - return; - } - - if !borrow_of_local_data(cmt) { - return; - } - - let scope = match *loan_region { - // A concrete region in which we will look for a yield expression - RegionKind::ReScope(scope) => scope, - - // There cannot be yields inside an empty region - RegionKind::ReEmpty => return, - - // Local data cannot have these lifetimes - RegionKind::ReEarlyBound(..) | - RegionKind::ReLateBound(..) | - RegionKind::ReFree(..) | - RegionKind::ReStatic => { - self.bccx - .tcx - .sess.delay_span_bug(borrow_span, - &format!("unexpected region for local data {:?}", - loan_region)); - return - } - - // These cannot exist in borrowck - RegionKind::ReVar(..) | - RegionKind::RePlaceholder(..) | - RegionKind::ReClosureBound(..) | - RegionKind::ReErased => span_bug!(borrow_span, - "unexpected region in borrowck {:?}", - loan_region), - }; - - let body_id = self.bccx.body.value.hir_id.local_id; - - if self.bccx.region_scope_tree.containing_body(scope) != Some(body_id) { - // We are borrowing local data longer than its storage. - // This should result in other borrowck errors. - self.bccx.tcx.sess.delay_span_bug(borrow_span, - "borrowing local data longer than its storage"); - return; - } - - if let Some(_) = self.bccx.region_scope_tree - .yield_in_scope_for_expr(scope, cmt.hir_id, self.bccx.body) - { - self.bccx.signal_error(); - } - } - - pub fn check_for_conflicting_loans(&self, node: hir::ItemLocalId) { - //! Checks to see whether any of the loans that are issued - //! on entrance to `node` conflict with loans that have already been - //! issued when we enter `node` (for example, we do not - //! permit two `&mut` borrows of the same variable). - //! - //! (Note that some loans can be *issued* without necessarily - //! taking effect yet.) - - debug!("check_for_conflicting_loans(node={:?})", node); - - let new_loan_indices = self.loans_generated_by(node); - debug!("new_loan_indices = {:?}", new_loan_indices); - - for &new_loan_index in &new_loan_indices { - self.each_issued_loan(node, |issued_loan| { - let new_loan = &self.all_loans[new_loan_index]; - // Only report an error for the first issued loan that conflicts - // to avoid O(n^2) errors. - self.report_error_if_loans_conflict(issued_loan, new_loan) - }); - } - - for (i, &x) in new_loan_indices.iter().enumerate() { - let old_loan = &self.all_loans[x]; - for &y in &new_loan_indices[(i+1) ..] { - let new_loan = &self.all_loans[y]; - self.report_error_if_loans_conflict(old_loan, new_loan); - } - } - } - - pub fn report_error_if_loans_conflict( - &self, - old_loan: &Loan<'tcx>, - new_loan: &Loan<'tcx>, - ) -> bool { - //! Checks whether `old_loan` and `new_loan` can safely be issued - //! simultaneously. - - debug!("report_error_if_loans_conflict(old_loan={:?}, new_loan={:?})", - old_loan, - new_loan); - - // Should only be called for loans that are in scope at the same time. - assert!(self.bccx.region_scope_tree.scopes_intersect(old_loan.kill_scope, - new_loan.kill_scope)); - - self.report_error_if_loan_conflicts_with_restriction( - old_loan, new_loan) - && self.report_error_if_loan_conflicts_with_restriction( - new_loan, old_loan) - } - - pub fn report_error_if_loan_conflicts_with_restriction( - &self, - loan1: &Loan<'tcx>, - loan2: &Loan<'tcx>, - ) -> bool { - //! Checks whether the restrictions introduced by `loan1` would - //! prohibit `loan2`. - debug!("report_error_if_loan_conflicts_with_restriction(\ - loan1={:?}, loan2={:?})", - loan1, - loan2); - - if compatible_borrow_kinds(loan1.kind, loan2.kind) { - return true; - } - - let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path); - for restr_path in &loan1.restricted_paths { - if *restr_path != loan2_base_path { continue; } - - self.bccx.signal_error(); - return false; - } - - true - } - - fn consume_common( - &self, - id: hir::ItemLocalId, - cmt: &mc::cmt_<'tcx>, - mode: euv::ConsumeMode, - ) { - if let Some(lp) = opt_loan_path(cmt) { - match mode { - euv::Copy => { - self.check_for_copy_of_frozen_path(id, &lp); - } - euv::Move(_) => { - // Sometimes moves aren't from a move path; - // this either means that the original move - // was from something illegal to move, - // or was moved from referent of an unsafe - // pointer or something like that. - if self.move_data.is_move_path(id, &lp) { - self.check_for_move_of_borrowed_path(id, &lp); - } - } - } - self.check_if_path_is_moved(id, &lp); - } - } - - fn check_for_copy_of_frozen_path(&self, - id: hir::ItemLocalId, - copy_path: &LoanPath<'tcx>) { - self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow); - } - - fn check_for_move_of_borrowed_path(&self, - id: hir::ItemLocalId, - move_path: &LoanPath<'tcx>) { - // We want to detect if there are any loans at all, so we search for - // any loans incompatible with MutBorrrow, since all other kinds of - // loans are incompatible with that. - self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow); - } - - fn analyze_restrictions_on_use(&self, - expr_id: hir::ItemLocalId, - use_path: &LoanPath<'tcx>, - borrow_kind: ty::BorrowKind) { - debug!("analyze_restrictions_on_use(expr_id={:?}, use_path={:?})", - expr_id, use_path); - - let scope = region::Scope { - id: expr_id, - data: region::ScopeData::Node - }; - self.each_in_scope_loan_affecting_path( - scope, use_path, |loan| { - if !compatible_borrow_kinds(loan.kind, borrow_kind) { - self.bccx.signal_error(); - false - } else { - true - } - }); - } - - /// Reports an error if `expr` (which should be a path) - /// is using a moved/uninitialized value - fn check_if_path_is_moved(&self, - id: hir::ItemLocalId, - lp: &Rc<LoanPath<'tcx>>) { - debug!("check_if_path_is_moved(id={:?}, lp={:?})", id, lp); - - // FIXME: if you find yourself tempted to cut and paste - // the body below and then specializing the error reporting, - // consider refactoring this instead! - - let base_lp = owned_ptr_base_path_rc(lp); - self.move_data.each_move_of(id, &base_lp, |_, _| { - self.bccx.signal_error(); - false - }); - } - - /// Reports an error if assigning to `lp` will use a - /// moved/uninitialized value. Mainly this is concerned with - /// detecting derefs of uninitialized pointers. - /// - /// For example: - /// - /// ``` - /// let a: i32; - /// a = 10; // ok, even though a is uninitialized - /// ``` - /// - /// ``` - /// struct Point { x: u32, y: u32 } - /// let mut p: Point; - /// p.x = 22; // ok, even though `p` is uninitialized - /// ``` - /// - /// ```compile_fail,E0381 - /// # struct Point { x: u32, y: u32 } - /// let mut p: Box<Point>; - /// (*p).x = 22; // not ok, p is uninitialized, can't deref - /// ``` - fn check_if_assigned_path_is_moved(&self, - id: hir::ItemLocalId, - lp: &Rc<LoanPath<'tcx>>) - { - match lp.kind { - LpVar(_) | LpUpvar(_) => { - // assigning to `x` does not require that `x` is initialized - } - LpDowncast(ref lp_base, _) => { - // assigning to `(P->Variant).f` is ok if assigning to `P` is ok - self.check_if_assigned_path_is_moved(id, lp_base); - } - LpExtend(ref lp_base, _, LpInterior(_, InteriorField(_))) => { - match lp_base.to_type().sty { - ty::Adt(def, _) if def.has_dtor(self.tcx()) => { - // In the case where the owner implements drop, then - // the path must be initialized to prevent a case of - // partial reinitialization - // - // FIXME: could refactor via hypothetical - // generalized check_if_path_is_moved - let loan_path = owned_ptr_base_path_rc(lp_base); - self.move_data.each_move_of(id, &loan_path, |_, _| { - self.bccx - .signal_error(); - false - }); - return; - }, - _ => {}, - } - - // assigning to `P.f` is ok if assigning to `P` is ok - self.check_if_assigned_path_is_moved(id, lp_base); - } - LpExtend(ref lp_base, _, LpInterior(_, InteriorElement)) | - LpExtend(ref lp_base, _, LpDeref(_)) => { - // assigning to `P[i]` requires `P` is initialized - // assigning to `(*P)` requires `P` is initialized - self.check_if_path_is_moved(id, lp_base); - } - } - } - - fn check_assignment(&self, - assignment_id: hir::ItemLocalId, - assignee_cmt: &mc::cmt_<'tcx>) { - debug!("check_assignment(assignee_cmt={:?})", assignee_cmt); - - // Check that we don't invalidate any outstanding loans - if let Some(loan_path) = opt_loan_path(assignee_cmt) { - let scope = region::Scope { - id: assignment_id, - data: region::ScopeData::Node - }; - self.each_in_scope_loan_affecting_path(scope, &loan_path, |_| { - self.bccx.signal_error(); - false - }); - } - - // Check for reassignments to (immutable) local variables. This - // needs to be done here instead of in check_loans because we - // depend on move data. - if let Categorization::Local(_) = assignee_cmt.cat { - let lp = opt_loan_path(assignee_cmt).unwrap(); - self.move_data.each_assignment_of(assignment_id, &lp, |_| { - if !assignee_cmt.mutbl.is_mutable() { - self.bccx.signal_error(); - } - false - }); - return - } - } -}
diff --git a/src/librustc_ast_borrowck/borrowck/gather_loans/gather_moves.rs b/src/librustc_ast_borrowck/borrowck/gather_loans/gather_moves.rs deleted file mode 100644 index 6171611..0000000 --- a/src/librustc_ast_borrowck/borrowck/gather_loans/gather_moves.rs +++ /dev/null
@@ -1,135 +0,0 @@ -//! Computes moves. - -use crate::borrowck::*; -use crate::borrowck::move_data::*; -use rustc::middle::mem_categorization as mc; -use rustc::middle::mem_categorization::Categorization; -use rustc::middle::mem_categorization::InteriorOffsetKind as Kind; -use rustc::ty::{self, Ty}; - -use std::rc::Rc; -use syntax_pos::Span; -use log::debug; - -struct GatherMoveInfo<'c, 'tcx> { - id: hir::ItemLocalId, - cmt: &'c mc::cmt_<'tcx>, -} - -pub fn gather_decl<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, - move_data: &MoveData<'tcx>, - var_id: hir::HirId, - var_ty: Ty<'tcx>) { - let loan_path = Rc::new(LoanPath::new(LpVar(var_id), var_ty)); - move_data.add_move(bccx.tcx, loan_path, var_id.local_id); -} - -pub fn gather_move_from_expr<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, - move_data: &MoveData<'tcx>, - move_expr_id: hir::ItemLocalId, - cmt: &mc::cmt_<'tcx>) { - let move_info = GatherMoveInfo { - id: move_expr_id, - cmt, - }; - gather_move(bccx, move_data, move_info); -} - -pub fn gather_move_from_pat<'a, 'c, 'tcx>( - bccx: &BorrowckCtxt<'a, 'tcx>, - move_data: &MoveData<'tcx>, - move_pat: &hir::Pat, - cmt: &'c mc::cmt_<'tcx>, -) { - let move_info = GatherMoveInfo { - id: move_pat.hir_id.local_id, - cmt, - }; - - debug!("gather_move_from_pat: move_pat={:?}", move_pat); - - gather_move(bccx, move_data, move_info); -} - -fn gather_move<'a, 'c, 'tcx>( - bccx: &BorrowckCtxt<'a, 'tcx>, - move_data: &MoveData<'tcx>, - move_info: GatherMoveInfo<'c, 'tcx>, -) { - debug!("gather_move(move_id={:?}, cmt={:?})", - move_info.id, move_info.cmt); - - let potentially_illegal_move = check_and_get_illegal_move_origin(bccx, move_info.cmt); - if let Some(_) = potentially_illegal_move { - bccx.signal_error(); - return; - } - - match opt_loan_path(&move_info.cmt) { - Some(loan_path) => { - move_data.add_move(bccx.tcx, loan_path, - move_info.id); - } - None => { - // move from rvalue or raw pointer, hence ok - } - } -} - -pub fn gather_assignment<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, - move_data: &MoveData<'tcx>, - assignment_id: hir::ItemLocalId, - assignment_span: Span, - assignee_loan_path: Rc<LoanPath<'tcx>>) { - move_data.add_assignment(bccx.tcx, - assignee_loan_path, - assignment_id, - assignment_span); -} - -// (keep in sync with move_error::report_cannot_move_out_of ) -fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, - cmt: &mc::cmt_<'tcx>) - -> Option<mc::cmt_<'tcx>> { - match cmt.cat { - Categorization::Deref(_, mc::BorrowedPtr(..)) | - Categorization::Deref(_, mc::UnsafePtr(..)) | - Categorization::ThreadLocal(..) | - Categorization::StaticItem => { - Some(cmt.clone()) - } - - Categorization::Rvalue(..) | - Categorization::Local(..) | - Categorization::Upvar(..) => { - None - } - - Categorization::Downcast(ref b, _) | - Categorization::Interior(ref b, mc::InteriorField(_)) | - Categorization::Interior(ref b, mc::InteriorElement(Kind::Pattern)) => { - match b.ty.sty { - ty::Adt(def, _) => { - if def.has_dtor(bccx.tcx) { - Some(cmt.clone()) - } else { - check_and_get_illegal_move_origin(bccx, b) - } - } - ty::Slice(..) => Some(cmt.clone()), - _ => { - check_and_get_illegal_move_origin(bccx, b) - } - } - } - - Categorization::Interior(_, mc::InteriorElement(Kind::Index)) => { - // Forbid move of arr[i] for arr: [T; 3]; see RFC 533. - Some(cmt.clone()) - } - - Categorization::Deref(ref b, mc::Unique) => { - check_and_get_illegal_move_origin(bccx, b) - } - } -}
diff --git a/src/librustc_ast_borrowck/borrowck/gather_loans/lifetime.rs b/src/librustc_ast_borrowck/borrowck/gather_loans/lifetime.rs deleted file mode 100644 index ff7dd66..0000000 --- a/src/librustc_ast_borrowck/borrowck/gather_loans/lifetime.rs +++ /dev/null
@@ -1,113 +0,0 @@ -//! This module implements the check that the lifetime of a borrow -//! does not exceed the lifetime of the value being borrowed. - -use crate::borrowck::*; -use rustc::hir::HirId; -use rustc::middle::mem_categorization as mc; -use rustc::middle::mem_categorization::Categorization; -use rustc::middle::region; -use rustc::ty; - -use log::debug; - -type R = Result<(),()>; - -pub fn guarantee_lifetime<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, - item_scope: region::Scope, - cmt: &'a mc::cmt_<'tcx>, - loan_region: ty::Region<'tcx>) - -> Result<(),()> { - //! Reports error if `loan_region` is larger than S - //! where S is `item_scope` if `cmt` is an upvar, - //! and is scope of `cmt` otherwise. - debug!("guarantee_lifetime(cmt={:?}, loan_region={:?})", - cmt, loan_region); - let ctxt = GuaranteeLifetimeContext { bccx, item_scope, loan_region }; - ctxt.check(cmt, None) -} - -/////////////////////////////////////////////////////////////////////////// -// Private - -struct GuaranteeLifetimeContext<'a, 'tcx> { - bccx: &'a BorrowckCtxt<'a, 'tcx>, - - // the scope of the function body for the enclosing item - item_scope: region::Scope, - - loan_region: ty::Region<'tcx>, -} - -impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> { - fn check(&self, cmt: &mc::cmt_<'tcx>, discr_scope: Option<HirId>) -> R { - //! Main routine. Walks down `cmt` until we find the - //! "guarantor". Reports an error if `self.loan_region` is - //! larger than scope of `cmt`. - debug!("guarantee_lifetime.check(cmt={:?}, loan_region={:?})", - cmt, - self.loan_region); - - match cmt.cat { - Categorization::Rvalue(..) | - Categorization::ThreadLocal(..) | - Categorization::Local(..) | // L-Local - Categorization::Upvar(..) | - Categorization::Deref(_, mc::BorrowedPtr(..)) | // L-Deref-Borrowed - Categorization::Deref(_, mc::UnsafePtr(..)) => { - self.check_scope(self.scope(cmt)) - } - - Categorization::StaticItem => { - Ok(()) - } - - Categorization::Downcast(ref base, _) | - Categorization::Deref(ref base, mc::Unique) | // L-Deref-Send - Categorization::Interior(ref base, _) => { // L-Field - self.check(base, discr_scope) - } - } - } - - fn check_scope(&self, max_scope: ty::Region<'tcx>) -> R { - //! Reports an error if `loan_region` is larger than `max_scope` - - if !self.bccx.is_subregion_of(self.loan_region, max_scope) { - Err(self.bccx.signal_error()) - } else { - Ok(()) - } - } - - fn scope(&self, cmt: &mc::cmt_<'tcx>) -> ty::Region<'tcx> { - //! Returns the maximal region scope for the which the - //! place `cmt` is guaranteed to be valid without any - //! rooting etc, and presuming `cmt` is not mutated. - - match cmt.cat { - Categorization::ThreadLocal(temp_scope) | - Categorization::Rvalue(temp_scope) => { - temp_scope - } - Categorization::Upvar(..) => { - self.bccx.tcx.mk_region(ty::ReScope(self.item_scope)) - } - Categorization::Local(hir_id) => { - self.bccx.tcx.mk_region(ty::ReScope( - self.bccx.region_scope_tree.var_scope(hir_id.local_id))) - } - Categorization::StaticItem | - Categorization::Deref(_, mc::UnsafePtr(..)) => { - self.bccx.tcx.lifetimes.re_static - } - Categorization::Deref(_, mc::BorrowedPtr(_, r)) => { - r - } - Categorization::Downcast(ref cmt, _) | - Categorization::Deref(ref cmt, mc::Unique) | - Categorization::Interior(ref cmt, _) => { - self.scope(cmt) - } - } - } -}
diff --git a/src/librustc_ast_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_ast_borrowck/borrowck/gather_loans/mod.rs deleted file mode 100644 index 16fef70..0000000 --- a/src/librustc_ast_borrowck/borrowck/gather_loans/mod.rs +++ /dev/null
@@ -1,433 +0,0 @@ -// ---------------------------------------------------------------------- -// Gathering loans -// -// The borrow check proceeds in two phases. In phase one, we gather the full -// set of loans that are required at any point. These are sorted according to -// their associated scopes. In phase two, checking loans, we will then make -// sure that all of these loans are honored. - -use crate::borrowck::*; -use crate::borrowck::move_data::MoveData; -use rustc::middle::expr_use_visitor as euv; -use rustc::middle::mem_categorization as mc; -use rustc::middle::mem_categorization::Categorization; -use rustc::middle::region; -use rustc::ty::{self, TyCtxt}; - -use syntax_pos::Span; -use rustc::hir; -use log::debug; - -use restrictions::RestrictionResult; - -mod lifetime; -mod restrictions; -mod gather_moves; - -pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, - body: hir::BodyId) - -> (Vec<Loan<'tcx>>, move_data::MoveData<'tcx>) { - let def_id = bccx.tcx.hir().body_owner_def_id(body); - let param_env = bccx.tcx.param_env(def_id); - let mut glcx = GatherLoanCtxt { - bccx, - all_loans: Vec::new(), - item_ub: region::Scope { - id: bccx.tcx.hir().body(body).value.hir_id.local_id, - data: region::ScopeData::Node - }, - move_data: MoveData::default(), - }; - - let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id); - euv::ExprUseVisitor::new(&mut glcx, - bccx.tcx, - def_id, - param_env, - &bccx.region_scope_tree, - bccx.tables, - Some(rvalue_promotable_map)) - .consume_body(bccx.body); - - let GatherLoanCtxt { all_loans, move_data, .. } = glcx; - (all_loans, move_data) -} - -struct GatherLoanCtxt<'a, 'tcx> { - bccx: &'a BorrowckCtxt<'a, 'tcx>, - move_data: move_data::MoveData<'tcx>, - all_loans: Vec<Loan<'tcx>>, - /// `item_ub` is used as an upper-bound on the lifetime whenever we - /// ask for the scope of an expression categorized as an upvar. - item_ub: region::Scope, -} - -impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> { - fn consume(&mut self, - consume_id: hir::HirId, - _consume_span: Span, - cmt: &mc::cmt_<'tcx>, - mode: euv::ConsumeMode) { - debug!("consume(consume_id={}, cmt={:?}, mode={:?})", - consume_id, cmt, mode); - - match mode { - euv::Move(_) => { - gather_moves::gather_move_from_expr( - self.bccx, &self.move_data, - consume_id.local_id, cmt); - } - euv::Copy => { } - } - } - - fn matched_pat(&mut self, - matched_pat: &hir::Pat, - cmt: &mc::cmt_<'tcx>, - mode: euv::MatchMode) { - debug!("matched_pat(matched_pat={:?}, cmt={:?}, mode={:?})", - matched_pat, - cmt, - mode); - } - - fn consume_pat(&mut self, - consume_pat: &hir::Pat, - cmt: &mc::cmt_<'tcx>, - mode: euv::ConsumeMode) { - debug!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})", - consume_pat, - cmt, - mode); - - match mode { - euv::Copy => { return; } - euv::Move(_) => { } - } - - gather_moves::gather_move_from_pat( - self.bccx, &self.move_data, - consume_pat, cmt); - } - - fn borrow(&mut self, - borrow_id: hir::HirId, - _: Span, - cmt: &mc::cmt_<'tcx>, - loan_region: ty::Region<'tcx>, - bk: ty::BorrowKind, - loan_cause: euv::LoanCause) - { - debug!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \ - bk={:?}, loan_cause={:?})", - borrow_id, cmt, loan_region, - bk, loan_cause); - - self.guarantee_valid(borrow_id.local_id, - cmt, - bk, - loan_region); - } - - fn mutate(&mut self, - assignment_id: hir::HirId, - assignment_span: Span, - assignee_cmt: &mc::cmt_<'tcx>, - _: euv::MutateMode) - { - self.guarantee_assignment_valid(assignment_id, - assignment_span, - assignee_cmt); - } - - fn decl_without_init(&mut self, id: hir::HirId, _span: Span) { - let ty = self.bccx - .tables - .node_type(id); - gather_moves::gather_decl(self.bccx, &self.move_data, id, ty); - } - - fn nested_body(&mut self, body_id: hir::BodyId) { - debug!("nested_body(body_id={:?})", body_id); - // rust-lang/rust#58776: MIR and AST borrow check disagree on where - // certain closure errors are reported. As such migrate borrowck has to - // operate at the level of items, rather than bodies. Check if the - // contained closure had any errors and set `signalled_any_error` if it - // has. - let bccx = self.bccx; - if bccx.tcx.migrate_borrowck() { - if let SignalledError::NoErrorsSeen = bccx.signalled_any_error.get() { - let closure_def_id = bccx.tcx.hir().body_owner_def_id(body_id); - debug!("checking closure: {:?}", closure_def_id); - - bccx.signalled_any_error.set(bccx.tcx.borrowck(closure_def_id).signalled_any_error); - } - } - } -} - -/// Implements the A-* rules in README.md. -fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, - cmt: &mc::cmt_<'tcx>, - req_kind: ty::BorrowKind) - -> Result<(),()> { - - let aliasability = cmt.freely_aliasable(); - debug!("check_aliasability aliasability={:?} req_kind={:?}", - aliasability, req_kind); - - match (aliasability, req_kind) { - (mc::Aliasability::NonAliasable, _) => { - /* Uniquely accessible path -- OK for `&` and `&mut` */ - Ok(()) - } - (mc::Aliasability::FreelyAliasable(mc::AliasableStatic), ty::ImmBorrow) => { - // Borrow of an immutable static item. - Ok(()) - } - (mc::Aliasability::FreelyAliasable(mc::AliasableStaticMut), _) => { - // Even touching a static mut is considered unsafe. We assume the - // user knows what they're doing in these cases. - Ok(()) - } - (mc::Aliasability::FreelyAliasable(_), ty::UniqueImmBorrow) | - (mc::Aliasability::FreelyAliasable(_), ty::MutBorrow) => { - bccx.signal_error(); - Err(()) - } - (..) => { - Ok(()) - } - } -} - -/// Implements the M-* rules in README.md. -fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, - cmt: &mc::cmt_<'tcx>, - req_kind: ty::BorrowKind) - -> Result<(),()> { - debug!("check_mutability(cmt={:?} req_kind={:?}", cmt, req_kind); - match req_kind { - ty::UniqueImmBorrow | ty::ImmBorrow => { - match cmt.mutbl { - // I am intentionally leaving this here to help - // refactoring if, in the future, we should add new - // kinds of mutability. - mc::McImmutable | mc::McDeclared | mc::McInherited => { - // both imm and mut data can be lent as imm; - // for mutable data, this is a freeze - Ok(()) - } - } - } - - ty::MutBorrow => { - // Only mutable data can be lent as mutable. - if !cmt.mutbl.is_mutable() { - Err(bccx.signal_error()) - } else { - Ok(()) - } - } - } -} - -impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { - pub fn tcx(&self) -> TyCtxt<'tcx> { self.bccx.tcx } - - /// Guarantees that `cmt` is assignable, or reports an error. - fn guarantee_assignment_valid(&mut self, - assignment_id: hir::HirId, - assignment_span: Span, - cmt: &mc::cmt_<'tcx>) { - - let opt_lp = opt_loan_path(cmt); - debug!("guarantee_assignment_valid(assignment_id={}, cmt={:?}) opt_lp={:?}", - assignment_id, cmt, opt_lp); - - if let Categorization::Local(..) = cmt.cat { - // Only re-assignments to locals require it to be - // mutable - this is checked in check_loans. - } else { - // Check that we don't allow assignments to non-mutable data. - if check_mutability(self.bccx, cmt, ty::MutBorrow).is_err() { - return; // reported an error, no sense in reporting more. - } - } - - // Check that we don't allow assignments to aliasable data - if check_aliasability(self.bccx, cmt, ty::MutBorrow).is_err() { - return; // reported an error, no sense in reporting more. - } - - match opt_lp { - Some(lp) => { - gather_moves::gather_assignment(self.bccx, &self.move_data, - assignment_id.local_id, - assignment_span, - lp); - } - None => { - // This can occur with e.g., `*foo() = 5`. In such - // cases, there is no need to check for conflicts - // with moves etc, just ignore. - } - } - } - - /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or - /// reports an error. This may entail taking out loans, which will be added to the - /// `req_loan_map`. - fn guarantee_valid(&mut self, - borrow_id: hir::ItemLocalId, - cmt: &mc::cmt_<'tcx>, - req_kind: ty::BorrowKind, - loan_region: ty::Region<'tcx>) { - debug!("guarantee_valid(borrow_id={:?}, cmt={:?}, \ - req_mutbl={:?}, loan_region={:?})", - borrow_id, - cmt, - req_kind, - loan_region); - - // a loan for the empty region can never be dereferenced, so - // it is always safe - if *loan_region == ty::ReEmpty { - return; - } - - // Check that the lifetime of the borrow does not exceed - // the lifetime of the data being borrowed. - if lifetime::guarantee_lifetime(self.bccx, self.item_ub, cmt, loan_region).is_err() { - return; // reported an error, no sense in reporting more. - } - - // Check that we don't allow mutable borrows of non-mutable data. - if check_mutability(self.bccx, cmt, req_kind).is_err() { - return; // reported an error, no sense in reporting more. - } - - // Check that we don't allow mutable borrows of aliasable data. - if check_aliasability(self.bccx, cmt, req_kind).is_err() { - return; // reported an error, no sense in reporting more. - } - - // Compute the restrictions that are required to enforce the - // loan is safe. - let restr = restrictions::compute_restrictions(self.bccx, &cmt, loan_region); - - debug!("guarantee_valid(): restrictions={:?}", restr); - - // Create the loan record (if needed). - let loan = match restr { - RestrictionResult::Safe => { - // No restrictions---no loan record necessary - return; - } - - RestrictionResult::SafeIf(loan_path, restricted_paths) => { - let loan_scope = match *loan_region { - ty::ReScope(scope) => scope, - - ty::ReEarlyBound(ref br) => { - self.bccx.region_scope_tree.early_free_scope(self.tcx(), br) - } - - ty::ReFree(ref fr) => { - self.bccx.region_scope_tree.free_scope(self.tcx(), fr) - } - - ty::ReStatic => self.item_ub, - - ty::ReEmpty | - ty::ReClosureBound(..) | - ty::ReLateBound(..) | - ty::ReVar(..) | - ty::RePlaceholder(..) | - ty::ReErased => { - span_bug!( - cmt.span, - "invalid borrow lifetime: {:?}", - loan_region); - } - }; - debug!("loan_scope = {:?}", loan_scope); - - let borrow_scope = region::Scope { - id: borrow_id, - data: region::ScopeData::Node - }; - let gen_scope = self.compute_gen_scope(borrow_scope, loan_scope); - debug!("gen_scope = {:?}", gen_scope); - - let kill_scope = self.compute_kill_scope(loan_scope, &loan_path); - debug!("kill_scope = {:?}", kill_scope); - - Loan { - index: self.all_loans.len(), - loan_path, - kind: req_kind, - gen_scope, - kill_scope, - restricted_paths, - } - } - }; - - debug!("guarantee_valid(borrow_id={:?}), loan={:?}", - borrow_id, loan); - - // let loan_path = loan.loan_path; - // let loan_gen_scope = loan.gen_scope; - // let loan_kill_scope = loan.kill_scope; - self.all_loans.push(loan); - } - - pub fn compute_gen_scope(&self, - borrow_scope: region::Scope, - loan_scope: region::Scope) - -> region::Scope { - //! Determine when to introduce the loan. Typically the loan - //! is introduced at the point of the borrow, but in some cases, - //! notably method arguments, the loan may be introduced only - //! later, once it comes into scope. - - if self.bccx.region_scope_tree.is_subscope_of(borrow_scope, loan_scope) { - borrow_scope - } else { - loan_scope - } - } - - pub fn compute_kill_scope(&self, loan_scope: region::Scope, lp: &LoanPath<'tcx>) - -> region::Scope { - //! Determine when the loan restrictions go out of scope. - //! This is either when the lifetime expires or when the - //! local variable which roots the loan-path goes out of scope, - //! whichever happens faster. - //! - //! It may seem surprising that we might have a loan region - //! larger than the variable which roots the loan-path; this can - //! come about when variables of `&mut` type are re-borrowed, - //! as in this example: - //! - //! struct Foo { counter: u32 } - //! - //! fn counter<'a>(v: &'a mut Foo) -> &'a mut u32 { - //! &mut v.counter - //! } - //! - //! In this case, the reference (`'a`) outlives the - //! variable `v` that hosts it. Note that this doesn't come up - //! with immutable `&` pointers, because borrows of such pointers - //! do not require restrictions and hence do not cause a loan. - - let lexical_scope = lp.kill_scope(self.bccx); - if self.bccx.region_scope_tree.is_subscope_of(lexical_scope, loan_scope) { - lexical_scope - } else { - assert!(self.bccx.region_scope_tree.is_subscope_of(loan_scope, lexical_scope)); - loan_scope - } - } -}
diff --git a/src/librustc_ast_borrowck/borrowck/gather_loans/restrictions.rs b/src/librustc_ast_borrowck/borrowck/gather_loans/restrictions.rs deleted file mode 100644 index 545c27b..0000000 --- a/src/librustc_ast_borrowck/borrowck/gather_loans/restrictions.rs +++ /dev/null
@@ -1,179 +0,0 @@ -//! Computes the restrictions that result from a borrow. - -use crate::borrowck::*; -use rustc::middle::mem_categorization as mc; -use rustc::middle::mem_categorization::Categorization; -use rustc::ty; -use log::debug; - -use crate::borrowck::ToInteriorKind; - -use std::rc::Rc; - -#[derive(Debug)] -pub enum RestrictionResult<'tcx> { - Safe, - SafeIf(Rc<LoanPath<'tcx>>, Vec<Rc<LoanPath<'tcx>>>) -} - -pub fn compute_restrictions<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, - cmt: &mc::cmt_<'tcx>, - loan_region: ty::Region<'tcx>) - -> RestrictionResult<'tcx> { - let ctxt = RestrictionsContext { bccx, loan_region }; - - ctxt.restrict(cmt) -} - -/////////////////////////////////////////////////////////////////////////// -// Private - -struct RestrictionsContext<'a, 'tcx> { - bccx: &'a BorrowckCtxt<'a, 'tcx>, - loan_region: ty::Region<'tcx>, -} - -impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> { - fn restrict(&self, - cmt: &mc::cmt_<'tcx>) -> RestrictionResult<'tcx> { - debug!("restrict(cmt={:?})", cmt); - - let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty)); - - match cmt.cat.clone() { - Categorization::Rvalue(..) => { - // Effectively, rvalues are stored into a - // non-aliasable temporary on the stack. Since they - // are inherently non-aliasable, they can only be - // accessed later through the borrow itself and hence - // must inherently comply with its terms. - RestrictionResult::Safe - } - - Categorization::ThreadLocal(..) => { - // Thread-locals are statics that have a scope, with - // no underlying structure to provide restrictions. - RestrictionResult::Safe - } - - Categorization::Local(local_id) => { - // R-Variable, locally declared - let lp = new_lp(LpVar(local_id)); - RestrictionResult::SafeIf(lp.clone(), vec![lp]) - } - - Categorization::Upvar(mc::Upvar { id, .. }) => { - // R-Variable, captured into closure - let lp = new_lp(LpUpvar(id)); - RestrictionResult::SafeIf(lp.clone(), vec![lp]) - } - - Categorization::Downcast(cmt_base, _) => { - // When we borrow the interior of an enum, we have to - // ensure the enum itself is not mutated, because that - // could cause the type of the memory to change. - self.restrict(&cmt_base) - } - - Categorization::Interior(cmt_base, interior) => { - // R-Field - // - // Overwriting the base would not change the type of - // the memory, so no additional restrictions are - // needed. - let opt_variant_id = match cmt_base.cat { - Categorization::Downcast(_, variant_id) => Some(variant_id), - _ => None - }; - let interior = interior.cleaned(); - let base_ty = cmt_base.ty; - let result = self.restrict(&cmt_base); - // Borrowing one union field automatically borrows all its fields. - match base_ty.sty { - ty::Adt(adt_def, _) if adt_def.is_union() => match result { - RestrictionResult::Safe => RestrictionResult::Safe, - RestrictionResult::SafeIf(base_lp, mut base_vec) => { - for (i, field) in adt_def.non_enum_variant().fields.iter().enumerate() { - let field = InteriorKind::InteriorField( - mc::FieldIndex(i, field.ident.name) - ); - let field_ty = if field == interior { - cmt.ty - } else { - self.bccx.tcx.types.err // Doesn't matter - }; - let sibling_lp_kind = LpExtend(base_lp.clone(), cmt.mutbl, - LpInterior(opt_variant_id, field)); - let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty)); - base_vec.push(sibling_lp); - } - - let lp = new_lp(LpExtend(base_lp, cmt.mutbl, - LpInterior(opt_variant_id, interior))); - RestrictionResult::SafeIf(lp, base_vec) - } - }, - _ => self.extend(result, &cmt, LpInterior(opt_variant_id, interior)) - } - } - - Categorization::StaticItem => { - RestrictionResult::Safe - } - - Categorization::Deref(cmt_base, pk) => { - match pk { - mc::Unique => { - // R-Deref-Send-Pointer - // - // When we borrow the interior of a box, we - // cannot permit the base to be mutated, because that - // would cause the unique pointer to be freed. - // - // Eventually we should make these non-special and - // just rely on Deref<T> implementation. - let result = self.restrict(&cmt_base); - self.extend(result, &cmt, LpDeref(pk)) - } - mc::BorrowedPtr(bk, lt) => { - // R-Deref-[Mut-]Borrowed - if !self.bccx.is_subregion_of(self.loan_region, lt) { - self.bccx.signal_error(); - return RestrictionResult::Safe; - } - - match bk { - ty::ImmBorrow => RestrictionResult::Safe, - ty::MutBorrow | ty::UniqueImmBorrow => { - // R-Deref-Mut-Borrowed - // - // The referent can be aliased after the - // references lifetime ends (by a newly-unfrozen - // borrow). - let result = self.restrict(&cmt_base); - self.extend(result, &cmt, LpDeref(pk)) - } - } - } - // Borrowck is not relevant for raw pointers - mc::UnsafePtr(..) => RestrictionResult::Safe - } - } - } - } - - fn extend(&self, - result: RestrictionResult<'tcx>, - cmt: &mc::cmt_<'tcx>, - elem: LoanPathElem<'tcx>) -> RestrictionResult<'tcx> { - match result { - RestrictionResult::Safe => RestrictionResult::Safe, - RestrictionResult::SafeIf(base_lp, mut base_vec) => { - let v = LpExtend(base_lp, cmt.mutbl, elem); - let lp = Rc::new(LoanPath::new(v, cmt.ty)); - base_vec.push(lp.clone()); - RestrictionResult::SafeIf(lp, base_vec) - } - } - } -}
diff --git a/src/librustc_ast_borrowck/borrowck/mod.rs b/src/librustc_ast_borrowck/borrowck/mod.rs deleted file mode 100644 index 23d5480..0000000 --- a/src/librustc_ast_borrowck/borrowck/mod.rs +++ /dev/null
@@ -1,621 +0,0 @@ -//! See The Book chapter on the borrow checker for more details. - -#![allow(non_camel_case_types)] - -pub use LoanPathKind::*; -pub use LoanPathElem::*; - -use InteriorKind::*; - -use rustc::hir::HirId; -use rustc::hir::Node; -use rustc::middle::borrowck::{BorrowCheckResult, SignalledError}; -use rustc::hir::def_id::{DefId, LocalDefId}; -use rustc::middle::mem_categorization as mc; -use rustc::middle::mem_categorization::Categorization; -use rustc::middle::region; -use rustc::middle::free_region::RegionRelations; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::query::Providers; - -use std::borrow::Cow; -use std::cell::{Cell}; -use std::fmt; -use std::rc::Rc; -use std::hash::{Hash, Hasher}; -use log::debug; - -use rustc::hir; - -use crate::cfg; -use crate::dataflow::{DataFlowContext, BitwiseOperator, DataFlowOperator, KillFrom}; - -pub mod check_loans; - -pub mod gather_loans; - -pub mod move_data; - -#[derive(Clone, Copy)] -pub struct LoanDataFlowOperator; - -pub type LoanDataFlow<'tcx> = DataFlowContext<'tcx, LoanDataFlowOperator>; - -pub fn check_crate(tcx: TyCtxt<'_>) { - tcx.par_body_owners(|body_owner_def_id| { - tcx.ensure().borrowck(body_owner_def_id); - }); -} - -pub fn provide(providers: &mut Providers<'_>) { - *providers = Providers { - borrowck, - ..*providers - }; -} - -/// Collection of conclusions determined via borrow checker analyses. -pub struct AnalysisData<'tcx> { - pub all_loans: Vec<Loan<'tcx>>, - pub loans: DataFlowContext<'tcx, LoanDataFlowOperator>, - pub move_data: move_data::FlowedMoveData<'tcx>, -} - -fn borrowck(tcx: TyCtxt<'_>, owner_def_id: DefId) -> &BorrowCheckResult { - assert!(tcx.use_ast_borrowck() || tcx.migrate_borrowck()); - - debug!("borrowck(body_owner_def_id={:?})", owner_def_id); - - let signalled_error = tcx.check_match(owner_def_id); - if let SignalledError::SawSomeError = signalled_error { - return tcx.arena.alloc(BorrowCheckResult { - signalled_any_error: SignalledError::SawSomeError, - }) - } - - let owner_id = tcx.hir().as_local_hir_id(owner_def_id).unwrap(); - - match tcx.hir().get(owner_id) { - Node::Ctor(..) => { - // We get invoked with anything that has MIR, but some of - // those things (notably the synthesized constructors from - // tuple structs/variants) do not have an associated body - // and do not need borrowchecking. - return tcx.arena.alloc(BorrowCheckResult { - signalled_any_error: SignalledError::NoErrorsSeen, - }) - } - _ => { } - } - - let body_id = tcx.hir().body_owned_by(owner_id); - let tables = tcx.typeck_tables_of(owner_def_id); - let region_scope_tree = tcx.region_scope_tree(owner_def_id); - let body = tcx.hir().body(body_id); - let mut bccx = BorrowckCtxt { - tcx, - tables, - region_scope_tree, - owner_def_id, - body, - signalled_any_error: Cell::new(SignalledError::NoErrorsSeen), - }; - - // Eventually, borrowck will always read the MIR, but at the - // moment we do not. So, for now, we always force MIR to be - // constructed for a given fn, since this may result in errors - // being reported and we want that to happen. - // - // Note that `mir_validated` is a "stealable" result; the - // thief, `optimized_mir()`, forces borrowck, so we know that - // is not yet stolen. - tcx.ensure().mir_validated(owner_def_id); - - // option dance because you can't capture an uninitialized variable - // by mut-ref. - let mut cfg = None; - if let Some(AnalysisData { all_loans, - loans: loan_dfcx, - move_data: flowed_moves }) = - build_borrowck_dataflow_data(&mut bccx, false, body_id, - |bccx| { - cfg = Some(cfg::CFG::new(bccx.tcx, &body)); - cfg.as_mut().unwrap() - }) - { - check_loans::check_loans(&mut bccx, &loan_dfcx, &flowed_moves, &all_loans, body); - } - - tcx.arena.alloc(BorrowCheckResult { - signalled_any_error: bccx.signalled_any_error.into_inner(), - }) -} - -fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tcx>, - force_analysis: bool, - body_id: hir::BodyId, - get_cfg: F) - -> Option<AnalysisData<'tcx>> - where F: FnOnce(&mut BorrowckCtxt<'a, 'tcx>) -> &'c cfg::CFG -{ - // Check the body of fn items. - let (all_loans, move_data) = - gather_loans::gather_loans_in_fn(this, body_id); - - if !force_analysis && move_data.is_empty() && all_loans.is_empty() { - // large arrays of data inserted as constants can take a lot of - // time and memory to borrow-check - see issue #36799. However, - // they don't have places, so no borrow-check is actually needed. - // Recognize that case and skip borrow-checking. - debug!("skipping loan propagation for {:?} because of no loans", body_id); - return None; - } else { - debug!("propagating loans in {:?}", body_id); - } - - let cfg = get_cfg(this); - let mut loan_dfcx = - DataFlowContext::new(this.tcx, - "borrowck", - Some(this.body), - cfg, - LoanDataFlowOperator, - all_loans.len()); - for (loan_idx, loan) in all_loans.iter().enumerate() { - loan_dfcx.add_gen(loan.gen_scope.item_local_id(), loan_idx); - loan_dfcx.add_kill(KillFrom::ScopeEnd, - loan.kill_scope.item_local_id(), - loan_idx); - } - loan_dfcx.add_kills_from_flow_exits(cfg); - loan_dfcx.propagate(cfg, this.body); - - let flowed_moves = move_data::FlowedMoveData::new(move_data, - this, - cfg, - this.body); - - Some(AnalysisData { all_loans, - loans: loan_dfcx, - move_data:flowed_moves }) -} - -/// Accessor for introspective clients inspecting `AnalysisData` and -/// the `BorrowckCtxt` itself , e.g., the flowgraph visualizer. -pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>( - tcx: TyCtxt<'tcx>, - body_id: hir::BodyId, - cfg: &cfg::CFG) - -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'tcx>) -{ - let owner_id = tcx.hir().body_owner(body_id); - let owner_def_id = tcx.hir().local_def_id(owner_id); - let tables = tcx.typeck_tables_of(owner_def_id); - let region_scope_tree = tcx.region_scope_tree(owner_def_id); - let body = tcx.hir().body(body_id); - let mut bccx = BorrowckCtxt { - tcx, - tables, - region_scope_tree, - owner_def_id, - body, - signalled_any_error: Cell::new(SignalledError::NoErrorsSeen), - }; - - let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg); - (bccx, dataflow_data.unwrap()) -} - -// ---------------------------------------------------------------------- -// Type definitions - -pub struct BorrowckCtxt<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - - // tables for the current thing we are checking; set to - // Some in `borrowck_fn` and cleared later - tables: &'a ty::TypeckTables<'tcx>, - - region_scope_tree: &'tcx region::ScopeTree, - - owner_def_id: DefId, - - body: &'tcx hir::Body, - - signalled_any_error: Cell<SignalledError>, -} - - -impl<'a, 'tcx: 'a> BorrowckCtxt<'a, 'tcx> { - fn signal_error(&self) { - self.signalled_any_error.set(SignalledError::SawSomeError); - } -} - -/////////////////////////////////////////////////////////////////////////// -// Loans and loan paths - -/// Record of a loan that was issued. -pub struct Loan<'tcx> { - index: usize, - loan_path: Rc<LoanPath<'tcx>>, - kind: ty::BorrowKind, - restricted_paths: Vec<Rc<LoanPath<'tcx>>>, - - /// gen_scope indicates where loan is introduced. Typically the - /// loan is introduced at the point of the borrow, but in some - /// cases, notably method arguments, the loan may be introduced - /// only later, once it comes into scope. See also - /// `GatherLoanCtxt::compute_gen_scope`. - gen_scope: region::Scope, - - /// kill_scope indicates when the loan goes out of scope. This is - /// either when the lifetime expires or when the local variable - /// which roots the loan-path goes out of scope, whichever happens - /// faster. See also `GatherLoanCtxt::compute_kill_scope`. - kill_scope: region::Scope, -} - -impl<'tcx> Loan<'tcx> { - pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> { - self.loan_path.clone() - } -} - -#[derive(Eq)] -pub struct LoanPath<'tcx> { - kind: LoanPathKind<'tcx>, - ty: Ty<'tcx>, -} - -impl<'tcx> PartialEq for LoanPath<'tcx> { - fn eq(&self, that: &LoanPath<'tcx>) -> bool { - self.kind == that.kind - } -} - -impl<'tcx> Hash for LoanPath<'tcx> { - fn hash<H: Hasher>(&self, state: &mut H) { - self.kind.hash(state); - } -} - -#[derive(PartialEq, Eq, Hash, Debug)] -pub enum LoanPathKind<'tcx> { - LpVar(hir::HirId), // `x` in README.md - LpUpvar(ty::UpvarId), // `x` captured by-value into closure - LpDowncast(Rc<LoanPath<'tcx>>, DefId), // `x` downcast to particular enum variant - LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem<'tcx>) -} - -impl<'tcx> LoanPath<'tcx> { - fn new(kind: LoanPathKind<'tcx>, ty: Ty<'tcx>) -> LoanPath<'tcx> { - LoanPath { kind: kind, ty: ty } - } - - fn to_type(&self) -> Ty<'tcx> { self.ty } -} - -// FIXME (pnkfelix): See discussion here -// https://github.com/pnkfelix/rust/commit/ -// b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003 -const DOWNCAST_PRINTED_OPERATOR: &'static str = " as "; - -// A local, "cleaned" version of `mc::InteriorKind` that drops -// information that is not relevant to loan-path analysis. (In -// particular, the distinction between how precisely an array-element -// is tracked is irrelevant here.) -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub enum InteriorKind { - InteriorField(mc::FieldIndex), - InteriorElement, -} - -trait ToInteriorKind { fn cleaned(self) -> InteriorKind; } -impl ToInteriorKind for mc::InteriorKind { - fn cleaned(self) -> InteriorKind { - match self { - mc::InteriorField(name) => InteriorField(name), - mc::InteriorElement(_) => InteriorElement, - } - } -} - -// This can be: -// - a pointer dereference (`*P` in README.md) -// - a field reference, with an optional definition of the containing -// enum variant (`P.f` in README.md) -// `DefId` is present when the field is part of struct that is in -// a variant of an enum. For instance in: -// `enum E { X { foo: u32 }, Y { foo: u32 }}` -// each `foo` is qualified by the definitition id of the variant (`X` or `Y`). -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum LoanPathElem<'tcx> { - LpDeref(mc::PointerKind<'tcx>), - LpInterior(Option<DefId>, InteriorKind), -} - -fn closure_to_block(closure_id: LocalDefId, tcx: TyCtxt<'_>) -> HirId { - let closure_id = tcx.hir().local_def_id_to_hir_id(closure_id); - match tcx.hir().get(closure_id) { - Node::Expr(expr) => match expr.node { - hir::ExprKind::Closure(.., body_id, _, _) => { - body_id.hir_id - } - _ => { - bug!("encountered non-closure id: {}", closure_id) - } - }, - _ => bug!("encountered non-expr id: {}", closure_id) - } -} - -impl<'a, 'tcx> LoanPath<'tcx> { - pub fn kill_scope(&self, bccx: &BorrowckCtxt<'a, 'tcx>) -> region::Scope { - match self.kind { - LpVar(hir_id) => { - bccx.region_scope_tree.var_scope(hir_id.local_id) - } - LpUpvar(upvar_id) => { - let block_id = closure_to_block(upvar_id.closure_expr_id, bccx.tcx); - region::Scope { id: block_id.local_id, data: region::ScopeData::Node } - } - LpDowncast(ref base, _) | - LpExtend(ref base, ..) => base.kill_scope(bccx), - } - } -} - -// Avoid "cannot borrow immutable field `self.x` as mutable" as that implies that a field *can* be -// mutable independently of the struct it belongs to. (#35937) -pub fn opt_loan_path_is_field<'tcx>(cmt: &mc::cmt_<'tcx>) -> (Option<Rc<LoanPath<'tcx>>>, bool) { - let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty)); - - match cmt.cat { - Categorization::Rvalue(..) | - Categorization::ThreadLocal(..) | - Categorization::StaticItem => { - (None, false) - } - - Categorization::Local(id) => { - (Some(new_lp(LpVar(id))), false) - } - - Categorization::Upvar(mc::Upvar { id, .. }) => { - (Some(new_lp(LpUpvar(id))), false) - } - - Categorization::Deref(ref cmt_base, pk) => { - let lp = opt_loan_path_is_field(cmt_base); - (lp.0.map(|lp| { - new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk))) - }), lp.1) - } - - Categorization::Interior(ref cmt_base, ik) => { - (opt_loan_path(cmt_base).map(|lp| { - let opt_variant_id = match cmt_base.cat { - Categorization::Downcast(_, did) => Some(did), - _ => None - }; - new_lp(LpExtend(lp, cmt.mutbl, LpInterior(opt_variant_id, ik.cleaned()))) - }), true) - } - - Categorization::Downcast(ref cmt_base, variant_def_id) => { - let lp = opt_loan_path_is_field(cmt_base); - (lp.0.map(|lp| { - new_lp(LpDowncast(lp, variant_def_id)) - }), lp.1) - } - } -} - -/// Computes the `LoanPath` (if any) for a `cmt`. -/// Note that this logic is somewhat duplicated in -/// the method `compute()` found in `gather_loans::restrictions`, -/// which allows it to share common loan path pieces as it -/// traverses the CMT. -pub fn opt_loan_path<'tcx>(cmt: &mc::cmt_<'tcx>) -> Option<Rc<LoanPath<'tcx>>> { - opt_loan_path_is_field(cmt).0 -} - -/////////////////////////////////////////////////////////////////////////// -// Misc - -impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { - pub fn is_subregion_of(&self, - r_sub: ty::Region<'tcx>, - r_sup: ty::Region<'tcx>) - -> bool - { - let region_rels = RegionRelations::new(self.tcx, - self.owner_def_id, - &self.region_scope_tree, - &self.tables.free_region_map); - region_rels.is_subregion_of(r_sub, r_sup) - } - - pub fn append_loan_path_to_string(&self, - loan_path: &LoanPath<'tcx>, - out: &mut String) { - match loan_path.kind { - LpUpvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id: id }, closure_expr_id: _ }) => { - out.push_str(&self.tcx.hir().name(id).as_str()); - } - LpVar(id) => { - out.push_str(&self.tcx.hir().name(id).as_str()); - } - - LpDowncast(ref lp_base, variant_def_id) => { - out.push('('); - self.append_loan_path_to_string(&lp_base, out); - out.push_str(DOWNCAST_PRINTED_OPERATOR); - out.push_str(&self.tcx.def_path_str(variant_def_id)); - out.push(')'); - } - - LpExtend(ref lp_base, _, LpInterior(_, InteriorField(mc::FieldIndex(_, info)))) => { - self.append_autoderefd_loan_path_to_string(&lp_base, out); - out.push('.'); - out.push_str(&info.as_str()); - } - - LpExtend(ref lp_base, _, LpInterior(_, InteriorElement)) => { - self.append_autoderefd_loan_path_to_string(&lp_base, out); - out.push_str("[..]"); - } - - LpExtend(ref lp_base, _, LpDeref(_)) => { - out.push('*'); - self.append_loan_path_to_string(&lp_base, out); - } - } - } - - pub fn append_autoderefd_loan_path_to_string(&self, - loan_path: &LoanPath<'tcx>, - out: &mut String) { - match loan_path.kind { - LpExtend(ref lp_base, _, LpDeref(_)) => { - // For a path like `(*x).f` or `(*x)[3]`, autoderef - // rules would normally allow users to omit the `*x`. - // So just serialize such paths to `x.f` or x[3]` respectively. - self.append_autoderefd_loan_path_to_string(&lp_base, out) - } - - LpDowncast(ref lp_base, variant_def_id) => { - out.push('('); - self.append_autoderefd_loan_path_to_string(&lp_base, out); - out.push_str(DOWNCAST_PRINTED_OPERATOR); - out.push_str(&self.tcx.def_path_str(variant_def_id)); - out.push(')'); - } - - LpVar(..) | LpUpvar(..) | LpExtend(.., LpInterior(..)) => { - self.append_loan_path_to_string(loan_path, out) - } - } - } - - pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String { - let mut result = String::new(); - self.append_loan_path_to_string(loan_path, &mut result); - result - } - - pub fn cmt_to_cow_str(&self, cmt: &mc::cmt_<'tcx>) -> Cow<'static, str> { - cmt.descriptive_string(self.tcx) - } - - pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt_<'tcx>) -> String { - match opt_loan_path(cmt) { - Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)), - None => self.cmt_to_cow_str(cmt).into_owned(), - } - } -} - -impl BitwiseOperator for LoanDataFlowOperator { - #[inline] - fn join(&self, succ: usize, pred: usize) -> usize { - succ | pred // loans from both preds are in scope - } -} - -impl DataFlowOperator for LoanDataFlowOperator { - #[inline] - fn initial_value(&self) -> bool { - false // no loans in scope by default - } -} - -impl fmt::Debug for InteriorKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - InteriorField(mc::FieldIndex(_, info)) => write!(f, "{}", info), - InteriorElement => write!(f, "[]"), - } - } -} - -impl<'tcx> fmt::Debug for Loan<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})", - self.index, - self.loan_path, - self.kind, - self.gen_scope, - self.kill_scope, - self.restricted_paths) - } -} - -impl<'tcx> fmt::Debug for LoanPath<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.kind { - LpVar(id) => { - write!(f, "$({})", ty::tls::with(|tcx| tcx.hir().node_to_string(id))) - } - - LpUpvar(ty::UpvarId{ var_path: ty::UpvarPath {hir_id: var_id}, closure_expr_id }) => { - let s = ty::tls::with(|tcx| { - tcx.hir().node_to_string(var_id) - }); - write!(f, "$({} captured by id={:?})", s, closure_expr_id) - } - - LpDowncast(ref lp, variant_def_id) => { - let variant_str = if variant_def_id.is_local() { - ty::tls::with(|tcx| tcx.def_path_str(variant_def_id)) - } else { - format!("{:?}", variant_def_id) - }; - write!(f, "({:?}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str) - } - - LpExtend(ref lp, _, LpDeref(_)) => { - write!(f, "{:?}.*", lp) - } - - LpExtend(ref lp, _, LpInterior(_, ref interior)) => { - write!(f, "{:?}.{:?}", lp, interior) - } - } - } -} - -impl<'tcx> fmt::Display for LoanPath<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.kind { - LpVar(id) => { - write!(f, "$({})", ty::tls::with(|tcx| tcx.hir().hir_to_user_string(id))) - } - - LpUpvar(ty::UpvarId{ var_path: ty::UpvarPath { hir_id }, closure_expr_id: _ }) => { - let s = ty::tls::with(|tcx| { - tcx.hir().node_to_string(hir_id) - }); - write!(f, "$({} captured by closure)", s) - } - - LpDowncast(ref lp, variant_def_id) => { - let variant_str = if variant_def_id.is_local() { - ty::tls::with(|tcx| tcx.def_path_str(variant_def_id)) - } else { - format!("{:?}", variant_def_id) - }; - write!(f, "({}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str) - } - - LpExtend(ref lp, _, LpDeref(_)) => { - write!(f, "{}.*", lp) - } - - LpExtend(ref lp, _, LpInterior(_, ref interior)) => { - write!(f, "{}.{:?}", lp, interior) - } - } - } -}
diff --git a/src/librustc_ast_borrowck/borrowck/move_data.rs b/src/librustc_ast_borrowck/borrowck/move_data.rs deleted file mode 100644 index 67d8181..0000000 --- a/src/librustc_ast_borrowck/borrowck/move_data.rs +++ /dev/null
@@ -1,730 +0,0 @@ -//! Data structures used for tracking moves. Please see the extensive -//! comments in the section "Moves and initialization" in `README.md`. - -use crate::dataflow::{DataFlowContext, BitwiseOperator, DataFlowOperator, KillFrom}; - -use crate::borrowck::*; -use crate::cfg; -use rustc::ty::{self, TyCtxt}; -use rustc::util::nodemap::FxHashMap; - -use std::cell::RefCell; -use std::rc::Rc; -use std::usize; -use syntax_pos::Span; -use rustc::hir; -use log::debug; - -#[derive(Default)] -pub struct MoveData<'tcx> { - /// Move paths. See section "Move paths" in `README.md`. - pub paths: RefCell<Vec<MovePath<'tcx>>>, - - /// Cache of loan path to move path index, for easy lookup. - pub path_map: RefCell<FxHashMap<Rc<LoanPath<'tcx>>, MovePathIndex>>, - - /// Each move or uninitialized variable gets an entry here. - pub moves: RefCell<Vec<Move>>, - - /// Assignments to a variable, like `x = foo`. These are assigned - /// bits for dataflow, since we must track them to ensure that - /// immutable variables are assigned at most once along each path. - pub var_assignments: RefCell<Vec<Assignment>>, - - /// Assignments to a path, like `x.f = foo`. These are not - /// assigned dataflow bits, but we track them because they still - /// kill move bits. - pub path_assignments: RefCell<Vec<Assignment>>, -} - -pub struct FlowedMoveData<'tcx> { - pub move_data: MoveData<'tcx>, - - pub dfcx_moves: MoveDataFlow<'tcx>, - - // We could (and maybe should, for efficiency) combine both move - // and assign data flow into one, but this way it's easier to - // distinguish the bits that correspond to moves and assignments. - pub dfcx_assign: AssignDataFlow<'tcx>, -} - -/// Index into `MoveData.paths`, used like a pointer -#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct MovePathIndex(usize); - -impl MovePathIndex { - fn get(&self) -> usize { - let MovePathIndex(v) = *self; v - } -} - -impl Clone for MovePathIndex { - fn clone(&self) -> MovePathIndex { - MovePathIndex(self.get()) - } -} - -#[allow(non_upper_case_globals)] -const InvalidMovePathIndex: MovePathIndex = MovePathIndex(usize::MAX); - -/// Index into `MoveData.moves`, used like a pointer -#[derive(Copy, Clone, PartialEq)] -pub struct MoveIndex(usize); - -impl MoveIndex { - fn get(&self) -> usize { - let MoveIndex(v) = *self; v - } -} - -#[allow(non_upper_case_globals)] -const InvalidMoveIndex: MoveIndex = MoveIndex(usize::MAX); - -pub struct MovePath<'tcx> { - /// Loan path corresponding to this move path - pub loan_path: Rc<LoanPath<'tcx>>, - - /// Parent pointer, `InvalidMovePathIndex` if root - pub parent: MovePathIndex, - - /// Head of linked list of moves to this path, - /// `InvalidMoveIndex` if not moved - pub first_move: MoveIndex, - - /// First node in linked list of children, `InvalidMovePathIndex` if leaf - pub first_child: MovePathIndex, - - /// Next node in linked list of parent's children (siblings), - /// `InvalidMovePathIndex` if none. - pub next_sibling: MovePathIndex, -} - - -#[derive(Copy, Clone)] -pub struct Move { - /// Path being moved. - pub path: MovePathIndex, - - /// ID of node that is doing the move. - pub id: hir::ItemLocalId, - - /// Next node in linked list of moves from `path`, or `InvalidMoveIndex` - pub next_move: MoveIndex -} - -#[derive(Copy, Clone)] -pub struct Assignment { - /// Path being assigned. - pub path: MovePathIndex, - - /// ID where assignment occurs - pub id: hir::ItemLocalId, - - /// span of node where assignment occurs - pub span: Span, -} - -#[derive(Clone, Copy)] -pub struct MoveDataFlowOperator; - -pub type MoveDataFlow<'tcx> = DataFlowContext<'tcx, MoveDataFlowOperator>; - -#[derive(Clone, Copy)] -pub struct AssignDataFlowOperator; - -pub type AssignDataFlow<'tcx> = DataFlowContext<'tcx, AssignDataFlowOperator>; - -fn loan_path_is_precise(loan_path: &LoanPath<'_>) -> bool { - match loan_path.kind { - LpVar(_) | LpUpvar(_) => { - true - } - LpExtend(.., LpInterior(_, InteriorKind::InteriorElement)) => { - // Paths involving element accesses a[i] do not refer to a unique - // location, as there is no accurate tracking of the indices. - // - // (Paths involving element accesses via slice pattern bindings - // can in principle be tracked precisely, but that is future - // work. For now, continue claiming that they are imprecise.) - false - } - LpDowncast(ref lp_base, _) | - LpExtend(ref lp_base, ..) => { - loan_path_is_precise(&lp_base) - } - } -} - -impl MoveData<'tcx> { - /// Returns `true` if there are no trackable assignments or moves - /// in this move data -- that means that there is nothing that - /// could cause a borrow error. - pub fn is_empty(&self) -> bool { - self.moves.borrow().is_empty() && - self.path_assignments.borrow().is_empty() && - self.var_assignments.borrow().is_empty() - } - - pub fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath<'tcx>> { - (*self.paths.borrow())[index.get()].loan_path.clone() - } - - fn path_parent(&self, index: MovePathIndex) -> MovePathIndex { - (*self.paths.borrow())[index.get()].parent - } - - fn path_first_move(&self, index: MovePathIndex) -> MoveIndex { - (*self.paths.borrow())[index.get()].first_move - } - - /// Returns the index of first child, or `InvalidMovePathIndex` if - /// `index` is leaf. - fn path_first_child(&self, index: MovePathIndex) -> MovePathIndex { - (*self.paths.borrow())[index.get()].first_child - } - - fn path_next_sibling(&self, index: MovePathIndex) -> MovePathIndex { - (*self.paths.borrow())[index.get()].next_sibling - } - - fn set_path_first_move(&self, - index: MovePathIndex, - first_move: MoveIndex) { - (*self.paths.borrow_mut())[index.get()].first_move = first_move - } - - fn set_path_first_child(&self, - index: MovePathIndex, - first_child: MovePathIndex) { - (*self.paths.borrow_mut())[index.get()].first_child = first_child - } - - fn move_next_move(&self, index: MoveIndex) -> MoveIndex { - //! Type safe indexing operator - (*self.moves.borrow())[index.get()].next_move - } - - fn is_var_path(&self, index: MovePathIndex) -> bool { - //! True if `index` refers to a variable - self.path_parent(index) == InvalidMovePathIndex - } - - /// Returns the existing move path index for `lp`, if any, and otherwise adds a new index for - /// `lp` and any of its base paths that do not yet have an index. - pub fn move_path(&self, tcx: TyCtxt<'tcx>, lp: Rc<LoanPath<'tcx>>) -> MovePathIndex { - if let Some(&index) = self.path_map.borrow().get(&lp) { - return index; - } - - let index = match lp.kind { - LpVar(..) | LpUpvar(..) => { - let index = MovePathIndex(self.paths.borrow().len()); - - self.paths.borrow_mut().push(MovePath { - loan_path: lp.clone(), - parent: InvalidMovePathIndex, - first_move: InvalidMoveIndex, - first_child: InvalidMovePathIndex, - next_sibling: InvalidMovePathIndex, - }); - - index - } - - LpDowncast(ref base, _) | - LpExtend(ref base, ..) => { - let parent_index = self.move_path(tcx, base.clone()); - - let index = MovePathIndex(self.paths.borrow().len()); - - let next_sibling = self.path_first_child(parent_index); - self.set_path_first_child(parent_index, index); - - self.paths.borrow_mut().push(MovePath { - loan_path: lp.clone(), - parent: parent_index, - first_move: InvalidMoveIndex, - first_child: InvalidMovePathIndex, - next_sibling, - }); - - index - } - }; - - debug!("move_path(lp={:?}, index={:?})", - lp, - index); - - assert_eq!(index.get(), self.paths.borrow().len() - 1); - self.path_map.borrow_mut().insert(lp, index); - return index; - } - - fn existing_move_path(&self, lp: &Rc<LoanPath<'tcx>>) - -> Option<MovePathIndex> { - self.path_map.borrow().get(lp).cloned() - } - - fn existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>) - -> Vec<MovePathIndex> { - let mut result = vec![]; - self.add_existing_base_paths(lp, &mut result); - result - } - - /// Adds any existing move path indices for `lp` and any base paths of `lp` to `result`, but - /// does not add new move paths - fn add_existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>, - result: &mut Vec<MovePathIndex>) { - match self.path_map.borrow().get(lp).cloned() { - Some(index) => { - self.each_base_path(index, |p| { - result.push(p); - true - }); - } - None => { - match lp.kind { - LpVar(..) | LpUpvar(..) => { } - LpDowncast(ref b, _) | - LpExtend(ref b, ..) => { - self.add_existing_base_paths(b, result); - } - } - } - } - - } - - /// Adds a new move entry for a move of `lp` that occurs at location `id` with kind `kind`. - pub fn add_move( - &self, - tcx: TyCtxt<'tcx>, - orig_lp: Rc<LoanPath<'tcx>>, - id: hir::ItemLocalId, - ) { - // Moving one union field automatically moves all its fields. Also move siblings of - // all parent union fields, moves do not propagate upwards automatically. - let mut lp = orig_lp.clone(); - while let LpExtend(ref base_lp, mutbl, lp_elem) = lp.clone().kind { - if let (&ty::Adt(adt_def, _), LpInterior(opt_variant_id, interior)) - = (&base_lp.ty.sty, lp_elem) { - if adt_def.is_union() { - for (i, field) in adt_def.non_enum_variant().fields.iter().enumerate() { - let field = - InteriorKind::InteriorField(mc::FieldIndex(i, field.ident.name)); - if field != interior { - let sibling_lp_kind = - LpExtend(base_lp.clone(), mutbl, LpInterior(opt_variant_id, field)); - let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, tcx.types.err)); - self.add_move_helper(tcx, sibling_lp, id); - } - } - } - } - lp = base_lp.clone(); - } - - self.add_move_helper(tcx, orig_lp, id); - } - - fn add_move_helper( - &self, - tcx: TyCtxt<'tcx>, - lp: Rc<LoanPath<'tcx>>, - id: hir::ItemLocalId, - ) { - debug!("add_move(lp={:?}, id={:?})", lp, id); - - let path_index = self.move_path(tcx, lp); - let move_index = MoveIndex(self.moves.borrow().len()); - - let next_move = self.path_first_move(path_index); - self.set_path_first_move(path_index, move_index); - - self.moves.borrow_mut().push(Move { - path: path_index, - id, - next_move, - }); - } - - /// Adds a new record for an assignment to `lp` that occurs at location `id` with the given - /// `span`. - pub fn add_assignment( - &self, - tcx: TyCtxt<'tcx>, - lp: Rc<LoanPath<'tcx>>, - assign_id: hir::ItemLocalId, - span: Span, - ) { - // Assigning to one union field automatically assigns to all its fields. - if let LpExtend(ref base_lp, mutbl, LpInterior(opt_variant_id, interior)) = lp.kind { - if let ty::Adt(adt_def, _) = base_lp.ty.sty { - if adt_def.is_union() { - for (i, field) in adt_def.non_enum_variant().fields.iter().enumerate() { - let field = - InteriorKind::InteriorField(mc::FieldIndex(i, field.ident.name)); - let field_ty = if field == interior { - lp.ty - } else { - tcx.types.err // Doesn't matter - }; - let sibling_lp_kind = LpExtend(base_lp.clone(), mutbl, - LpInterior(opt_variant_id, field)); - let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty)); - self.add_assignment_helper(tcx, sibling_lp, assign_id, - span); - } - return; - } - } - } - - self.add_assignment_helper(tcx, lp, assign_id, span); - } - - fn add_assignment_helper( - &self, - tcx: TyCtxt<'tcx>, - lp: Rc<LoanPath<'tcx>>, - assign_id: hir::ItemLocalId, - span: Span, - ) { - debug!("add_assignment(lp={:?}, assign_id={:?}", lp, assign_id); - - let path_index = self.move_path(tcx, lp.clone()); - - let assignment = Assignment { - path: path_index, - id: assign_id, - span, - }; - - if self.is_var_path(path_index) { - debug!("add_assignment[var](lp={:?}, assignment={}, path_index={:?})", - lp, self.var_assignments.borrow().len(), path_index); - - self.var_assignments.borrow_mut().push(assignment); - } else { - debug!("add_assignment[path](lp={:?}, path_index={:?})", - lp, path_index); - - self.path_assignments.borrow_mut().push(assignment); - } - } - - /// Adds the gen/kills for the various moves and - /// assignments into the provided data flow contexts. - /// Moves are generated by moves and killed by assignments and - /// scoping. Assignments are generated by assignment to variables and - /// killed by scoping. See `README.md` for more details. - fn add_gen_kills( - &self, - bccx: &BorrowckCtxt<'_, 'tcx>, - dfcx_moves: &mut MoveDataFlow<'_>, - dfcx_assign: &mut AssignDataFlow<'_>, - ) { - for (i, the_move) in self.moves.borrow().iter().enumerate() { - dfcx_moves.add_gen(the_move.id, i); - } - - for (i, assignment) in self.var_assignments.borrow().iter().enumerate() { - dfcx_assign.add_gen(assignment.id, i); - self.kill_moves(assignment.path, assignment.id, - KillFrom::Execution, dfcx_moves); - } - - for assignment in self.path_assignments.borrow().iter() { - self.kill_moves(assignment.path, assignment.id, - KillFrom::Execution, dfcx_moves); - } - - // Kill all moves related to a variable `x` when - // it goes out of scope: - for path in self.paths.borrow().iter() { - match path.loan_path.kind { - LpVar(..) | LpUpvar(..) | LpDowncast(..) => { - let kill_scope = path.loan_path.kill_scope(bccx); - let path = *self.path_map.borrow().get(&path.loan_path).unwrap(); - self.kill_moves(path, kill_scope.item_local_id(), - KillFrom::ScopeEnd, dfcx_moves); - } - LpExtend(..) => {} - } - } - - // Kill all assignments when the variable goes out of scope: - for (assignment_index, assignment) in - self.var_assignments.borrow().iter().enumerate() { - let lp = self.path_loan_path(assignment.path); - match lp.kind { - LpVar(..) | LpUpvar(..) | LpDowncast(..) => { - let kill_scope = lp.kill_scope(bccx); - dfcx_assign.add_kill(KillFrom::ScopeEnd, - kill_scope.item_local_id(), - assignment_index); - } - LpExtend(..) => { - bug!("var assignment for non var path"); - } - } - } - } - - fn each_base_path<F>(&self, index: MovePathIndex, mut f: F) -> bool where - F: FnMut(MovePathIndex) -> bool, - { - let mut p = index; - while p != InvalidMovePathIndex { - if !f(p) { - return false; - } - p = self.path_parent(p); - } - return true; - } - - // FIXME(#19596) This is a workaround, but there should be better way to do this - fn each_extending_path_<F>(&self, index: MovePathIndex, f: &mut F) -> bool where - F: FnMut(MovePathIndex) -> bool, - { - if !(*f)(index) { - return false; - } - - let mut p = self.path_first_child(index); - while p != InvalidMovePathIndex { - if !self.each_extending_path_(p, f) { - return false; - } - p = self.path_next_sibling(p); - } - - return true; - } - - fn each_extending_path<F>(&self, index: MovePathIndex, mut f: F) -> bool where - F: FnMut(MovePathIndex) -> bool, - { - self.each_extending_path_(index, &mut f) - } - - fn each_applicable_move<F>(&self, index0: MovePathIndex, mut f: F) -> bool where - F: FnMut(MoveIndex) -> bool, - { - let mut ret = true; - self.each_extending_path(index0, |index| { - let mut p = self.path_first_move(index); - while p != InvalidMoveIndex { - if !f(p) { - ret = false; - break; - } - p = self.move_next_move(p); - } - ret - }); - ret - } - - fn kill_moves( - &self, - path: MovePathIndex, - kill_id: hir::ItemLocalId, - kill_kind: KillFrom, - dfcx_moves: &mut MoveDataFlow<'_>, - ) { - // We can only perform kills for paths that refer to a unique location, - // since otherwise we may kill a move from one location with an - // assignment referring to another location. - - let loan_path = self.path_loan_path(path); - if loan_path_is_precise(&loan_path) { - self.each_applicable_move(path, |move_index| { - debug!("kill_moves add_kill {:?} kill_id={:?} move_index={}", - kill_kind, kill_id, move_index.get()); - dfcx_moves.add_kill(kill_kind, kill_id, move_index.get()); - true - }); - } - } -} - -impl<'tcx> FlowedMoveData<'tcx> { - pub fn new( - move_data: MoveData<'tcx>, - bccx: &BorrowckCtxt<'_, 'tcx>, - cfg: &cfg::CFG, - body: &hir::Body, - ) -> FlowedMoveData<'tcx> { - let tcx = bccx.tcx; - - let mut dfcx_moves = - DataFlowContext::new(tcx, - "flowed_move_data_moves", - Some(body), - cfg, - MoveDataFlowOperator, - move_data.moves.borrow().len()); - let mut dfcx_assign = - DataFlowContext::new(tcx, - "flowed_move_data_assigns", - Some(body), - cfg, - AssignDataFlowOperator, - move_data.var_assignments.borrow().len()); - - move_data.add_gen_kills(bccx, - &mut dfcx_moves, - &mut dfcx_assign); - - dfcx_moves.add_kills_from_flow_exits(cfg); - dfcx_assign.add_kills_from_flow_exits(cfg); - - dfcx_moves.propagate(cfg, body); - dfcx_assign.propagate(cfg, body); - - FlowedMoveData { - move_data, - dfcx_moves, - dfcx_assign, - } - } - - pub fn is_move_path(&self, id: hir::ItemLocalId, loan_path: &Rc<LoanPath<'tcx>>) -> bool { - //! Returns the kind of a move of `loan_path` by `id`, if one exists. - - let mut ret = false; - if let Some(loan_path_index) = self.move_data.path_map.borrow().get(&*loan_path) { - self.dfcx_moves.each_gen_bit(id, |move_index| { - let the_move = self.move_data.moves.borrow(); - let the_move = (*the_move)[move_index]; - if the_move.path == *loan_path_index { - ret = true; - false - } else { - true - } - }); - } - ret - } - - /// Iterates through each move of `loan_path` (or some base path of `loan_path`) that *may* - /// have occurred on entry to `id` without an intervening assignment. In other words, any moves - /// that would invalidate a reference to `loan_path` at location `id`. - pub fn each_move_of<F>(&self, - id: hir::ItemLocalId, - loan_path: &Rc<LoanPath<'tcx>>, - mut f: F) - -> bool where - F: FnMut(&Move, &LoanPath<'tcx>) -> bool, - { - // Bad scenarios: - // - // 1. Move of `a.b.c`, use of `a.b.c` - // 2. Move of `a.b.c`, use of `a.b.c.d` - // 3. Move of `a.b.c`, use of `a` or `a.b` - // - // OK scenario: - // - // 4. move of `a.b.c`, use of `a.b.d` - - let base_indices = self.move_data.existing_base_paths(loan_path); - if base_indices.is_empty() { - return true; - } - - let opt_loan_path_index = self.move_data.existing_move_path(loan_path); - - let mut ret = true; - - self.dfcx_moves.each_bit_on_entry(id, |index| { - let the_move = self.move_data.moves.borrow(); - let the_move = &(*the_move)[index]; - let moved_path = the_move.path; - if base_indices.iter().any(|x| x == &moved_path) { - // Scenario 1 or 2: `loan_path` or some base path of - // `loan_path` was moved. - if !f(the_move, &self.move_data.path_loan_path(moved_path)) { - ret = false; - } - } else { - if let Some(loan_path_index) = opt_loan_path_index { - let cont = self.move_data.each_base_path(moved_path, |p| { - if p == loan_path_index { - // Scenario 3: some extension of `loan_path` - // was moved - f(the_move, - &self.move_data.path_loan_path(moved_path)) - } else { - true - } - }); - if !cont { ret = false; } - } - } - ret - }) - } - - /// Iterates through every assignment to `loan_path` that may have occurred on entry to `id`. - /// `loan_path` must be a single variable. - pub fn each_assignment_of<F>(&self, - id: hir::ItemLocalId, - loan_path: &Rc<LoanPath<'tcx>>, - mut f: F) - -> bool where - F: FnMut(&Assignment) -> bool, - { - let loan_path_index = { - match self.move_data.existing_move_path(loan_path) { - Some(i) => i, - None => { - // if there were any assignments, it'd have an index - return true; - } - } - }; - - self.dfcx_assign.each_bit_on_entry(id, |index| { - let assignment = self.move_data.var_assignments.borrow(); - let assignment = &(*assignment)[index]; - if assignment.path == loan_path_index && !f(assignment) { - false - } else { - true - } - }) - } -} - -impl BitwiseOperator for MoveDataFlowOperator { - #[inline] - fn join(&self, succ: usize, pred: usize) -> usize { - succ | pred // moves from both preds are in scope - } -} - -impl DataFlowOperator for MoveDataFlowOperator { - #[inline] - fn initial_value(&self) -> bool { - false // no loans in scope by default - } -} - -impl BitwiseOperator for AssignDataFlowOperator { - #[inline] - fn join(&self, succ: usize, pred: usize) -> usize { - succ | pred // moves from both preds are in scope - } -} - -impl DataFlowOperator for AssignDataFlowOperator { - #[inline] - fn initial_value(&self) -> bool { - false // no assignments in scope by default - } -}
diff --git a/src/librustc_ast_borrowck/cfg/construct.rs b/src/librustc_ast_borrowck/cfg/construct.rs deleted file mode 100644 index 0dc9990..0000000 --- a/src/librustc_ast_borrowck/cfg/construct.rs +++ /dev/null
@@ -1,545 +0,0 @@ -use crate::cfg::*; - -use rustc::hir::{self, PatKind}; -use rustc::hir::def_id::DefId; -use rustc::hir::ptr::P; -use rustc::middle::region; -use rustc::ty::{self, TyCtxt}; - -use rustc_data_structures::graph::implementation as graph; - -struct CFGBuilder<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - owner_def_id: DefId, - tables: &'a ty::TypeckTables<'tcx>, - graph: CFGGraph, - fn_exit: CFGIndex, - loop_scopes: Vec<LoopScope>, - breakable_block_scopes: Vec<BlockScope>, -} - -#[derive(Copy, Clone)] -struct BlockScope { - block_expr_id: hir::ItemLocalId, // ID of breakable block expr node - break_index: CFGIndex, // where to go on `break` -} - -#[derive(Copy, Clone)] -struct LoopScope { - loop_id: hir::ItemLocalId, // ID of `loop`/`while` node - continue_index: CFGIndex, // where to go on a `loop` - break_index: CFGIndex, // where to go on a `break` -} - -pub(super) fn construct(tcx: TyCtxt<'_>, body: &hir::Body) -> CFG { - let mut graph = graph::Graph::new(); - let entry = graph.add_node(CFGNodeData::Entry); - - // `fn_exit` is target of return exprs, which lies somewhere - // outside input `body`. (Distinguishing `fn_exit` and `body_exit` - // also resolves chicken-and-egg problem that arises if you try to - // have return exprs jump to `body_exit` during construction.) - let fn_exit = graph.add_node(CFGNodeData::Exit); - let body_exit; - - // Find the tables for this body. - let owner_def_id = tcx.hir().body_owner_def_id(body.id()); - let tables = tcx.typeck_tables_of(owner_def_id); - - let mut cfg_builder = CFGBuilder { - tcx, - owner_def_id, - tables, - graph, - fn_exit, - loop_scopes: Vec::new(), - breakable_block_scopes: Vec::new(), - }; - body_exit = cfg_builder.expr(&body.value, entry); - cfg_builder.add_contained_edge(body_exit, fn_exit); - let CFGBuilder { graph, .. } = cfg_builder; - CFG { - owner_def_id, - graph, - entry, - exit: fn_exit, - } -} - -impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { - fn block(&mut self, blk: &hir::Block, pred: CFGIndex) -> CFGIndex { - if blk.targeted_by_break { - let expr_exit = self.add_ast_node(blk.hir_id.local_id, &[]); - - self.breakable_block_scopes.push(BlockScope { - block_expr_id: blk.hir_id.local_id, - break_index: expr_exit, - }); - - let mut stmts_exit = pred; - for stmt in &blk.stmts { - stmts_exit = self.stmt(stmt, stmts_exit); - } - let blk_expr_exit = self.opt_expr(&blk.expr, stmts_exit); - self.add_contained_edge(blk_expr_exit, expr_exit); - - self.breakable_block_scopes.pop(); - - expr_exit - } else { - let mut stmts_exit = pred; - for stmt in &blk.stmts { - stmts_exit = self.stmt(stmt, stmts_exit); - } - - let expr_exit = self.opt_expr(&blk.expr, stmts_exit); - - self.add_ast_node(blk.hir_id.local_id, &[expr_exit]) - } - } - - fn stmt(&mut self, stmt: &hir::Stmt, pred: CFGIndex) -> CFGIndex { - let exit = match stmt.node { - hir::StmtKind::Local(ref local) => { - let init_exit = self.opt_expr(&local.init, pred); - self.pat(&local.pat, init_exit) - } - hir::StmtKind::Item(_) => pred, - hir::StmtKind::Expr(ref expr) | - hir::StmtKind::Semi(ref expr) => { - self.expr(&expr, pred) - } - }; - self.add_ast_node(stmt.hir_id.local_id, &[exit]) - } - - fn pat(&mut self, pat: &hir::Pat, pred: CFGIndex) -> CFGIndex { - match pat.node { - PatKind::Binding(.., None) | - PatKind::Path(_) | - PatKind::Lit(..) | - PatKind::Range(..) | - PatKind::Wild => self.add_ast_node(pat.hir_id.local_id, &[pred]), - - PatKind::Box(ref subpat) | - PatKind::Ref(ref subpat, _) | - PatKind::Binding(.., Some(ref subpat)) => { - let subpat_exit = self.pat(&subpat, pred); - self.add_ast_node(pat.hir_id.local_id, &[subpat_exit]) - } - - PatKind::TupleStruct(_, ref subpats, _) | - PatKind::Tuple(ref subpats, _) => { - let pats_exit = self.pats_all(subpats.iter(), pred); - self.add_ast_node(pat.hir_id.local_id, &[pats_exit]) - } - - PatKind::Struct(_, ref subpats, _) => { - let pats_exit = self.pats_all(subpats.iter().map(|f| &f.pat), pred); - self.add_ast_node(pat.hir_id.local_id, &[pats_exit]) - } - - PatKind::Or(ref pats) => { - let branches: Vec<_> = pats.iter().map(|p| self.pat(p, pred)).collect(); - self.add_ast_node(pat.hir_id.local_id, &branches) - } - - PatKind::Slice(ref pre, ref vec, ref post) => { - let pre_exit = self.pats_all(pre.iter(), pred); - let vec_exit = self.pats_all(vec.iter(), pre_exit); - let post_exit = self.pats_all(post.iter(), vec_exit); - self.add_ast_node(pat.hir_id.local_id, &[post_exit]) - } - } - } - - /// Handles case where all of the patterns must match. - fn pats_all<'b, I: Iterator<Item = &'b P<hir::Pat>>>( - &mut self, - pats: I, - pred: CFGIndex, - ) -> CFGIndex { - pats.fold(pred, |pred, pat| self.pat(&pat, pred)) - } - - fn expr(&mut self, expr: &hir::Expr, pred: CFGIndex) -> CFGIndex { - match expr.node { - hir::ExprKind::Block(ref blk, _) => { - let blk_exit = self.block(&blk, pred); - self.add_ast_node(expr.hir_id.local_id, &[blk_exit]) - } - - hir::ExprKind::Loop(ref body, _, _) => { - // - // [pred] - // | - // v 1 - // [loopback] <---+ - // | 4 | - // v 3 | - // [body] ------+ - // - // [expr] 2 - // - // Note that `break` and `loop` statements - // may cause additional edges. - - let loopback = self.add_dummy_node(&[pred]); // 1 - let expr_exit = self.add_ast_node(expr.hir_id.local_id, &[]); // 2 - self.loop_scopes.push(LoopScope { - loop_id: expr.hir_id.local_id, - continue_index: loopback, - break_index: expr_exit, - }); - let body_exit = self.block(&body, loopback); // 3 - self.add_contained_edge(body_exit, loopback); // 4 - self.loop_scopes.pop(); - expr_exit - } - - hir::ExprKind::Match(ref discr, ref arms, _) => { - self.match_(expr.hir_id.local_id, &discr, &arms, pred) - } - - hir::ExprKind::Binary(op, ref l, ref r) if op.node.is_lazy() => { - // - // [pred] - // | - // v 1 - // [l] - // | - // / \ - // / \ - // v 2 * - // [r] | - // | | - // v 3 v 4 - // [..exit..] - // - let l_exit = self.expr(&l, pred); // 1 - let r_exit = self.expr(&r, l_exit); // 2 - self.add_ast_node(expr.hir_id.local_id, &[l_exit, r_exit]) // 3,4 - } - - hir::ExprKind::Ret(ref v) => { - let v_exit = self.opt_expr(v, pred); - let b = self.add_ast_node(expr.hir_id.local_id, &[v_exit]); - self.add_returning_edge(expr, b); - self.add_unreachable_node() - } - - hir::ExprKind::Break(destination, ref opt_expr) => { - let v = self.opt_expr(opt_expr, pred); - let (target_scope, break_dest) = - self.find_scope_edge(expr, destination, ScopeCfKind::Break); - let b = self.add_ast_node(expr.hir_id.local_id, &[v]); - self.add_exiting_edge(expr, b, target_scope, break_dest); - self.add_unreachable_node() - } - - hir::ExprKind::Continue(destination) => { - let (target_scope, cont_dest) = - self.find_scope_edge(expr, destination, ScopeCfKind::Continue); - let a = self.add_ast_node(expr.hir_id.local_id, &[pred]); - self.add_exiting_edge(expr, a, target_scope, cont_dest); - self.add_unreachable_node() - } - - hir::ExprKind::Array(ref elems) => { - self.straightline(expr, pred, elems.iter().map(|e| &*e)) - } - - hir::ExprKind::Call(ref func, ref args) => { - self.call(expr, pred, &func, args.iter().map(|e| &*e)) - } - - hir::ExprKind::MethodCall(.., ref args) => { - self.call(expr, pred, &args[0], args[1..].iter().map(|e| &*e)) - } - - hir::ExprKind::Index(ref l, ref r) | - hir::ExprKind::Binary(_, ref l, ref r) if self.tables.is_method_call(expr) => { - self.call(expr, pred, &l, Some(&**r).into_iter()) - } - - hir::ExprKind::Unary(_, ref e) if self.tables.is_method_call(expr) => { - self.call(expr, pred, &e, None::<hir::Expr>.iter()) - } - - hir::ExprKind::Tup(ref exprs) => { - self.straightline(expr, pred, exprs.iter().map(|e| &*e)) - } - - hir::ExprKind::Struct(_, ref fields, ref base) => { - let field_cfg = self.straightline(expr, pred, fields.iter().map(|f| &*f.expr)); - self.opt_expr(base, field_cfg) - } - - hir::ExprKind::Assign(ref l, ref r) | - hir::ExprKind::AssignOp(_, ref l, ref r) => { - self.straightline(expr, pred, [r, l].iter().map(|&e| &**e)) - } - - hir::ExprKind::Index(ref l, ref r) | - hir::ExprKind::Binary(_, ref l, ref r) => { // N.B., && and || handled earlier - self.straightline(expr, pred, [l, r].iter().map(|&e| &**e)) - } - - hir::ExprKind::Box(ref e) | - hir::ExprKind::AddrOf(_, ref e) | - hir::ExprKind::Cast(ref e, _) | - hir::ExprKind::Type(ref e, _) | - hir::ExprKind::DropTemps(ref e) | - hir::ExprKind::Unary(_, ref e) | - hir::ExprKind::Field(ref e, _) | - hir::ExprKind::Yield(ref e, _) | - hir::ExprKind::Repeat(ref e, _) => { - self.straightline(expr, pred, Some(&**e).into_iter()) - } - - hir::ExprKind::InlineAsm(_, ref outputs, ref inputs) => { - let post_outputs = self.exprs(outputs.iter().map(|e| &*e), pred); - let post_inputs = self.exprs(inputs.iter().map(|e| &*e), post_outputs); - self.add_ast_node(expr.hir_id.local_id, &[post_inputs]) - } - - hir::ExprKind::Closure(..) | - hir::ExprKind::Lit(..) | - hir::ExprKind::Path(_) | - hir::ExprKind::Err => { - self.straightline(expr, pred, None::<hir::Expr>.iter()) - } - } - } - - fn call<'b, I: Iterator<Item = &'b hir::Expr>>( - &mut self, - call_expr: &hir::Expr, - pred: CFGIndex, - func_or_rcvr: &hir::Expr, - args: I, - ) -> CFGIndex { - let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); - let ret = self.straightline(call_expr, func_or_rcvr_exit, args); - let m = self.tcx.hir().get_module_parent(call_expr.hir_id); - if self.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(call_expr)) { - self.add_unreachable_node() - } else { - ret - } - } - - /// Constructs graph for `exprs` evaluated in order. - fn exprs<'b, I: Iterator<Item = &'b hir::Expr>>( - &mut self, - exprs: I, - pred: CFGIndex, - ) -> CFGIndex { - exprs.fold(pred, |p, e| self.expr(e, p)) - } - - /// Constructs graph for `opt_expr` evaluated, if `Some`. - fn opt_expr( - &mut self, - opt_expr: &Option<P<hir::Expr>>, - pred: CFGIndex, - ) -> CFGIndex { - opt_expr.iter().fold(pred, |p, e| self.expr(&e, p)) - } - - /// Handles case of an expression that evaluates `subexprs` in order. - fn straightline<'b, I: Iterator<Item = &'b hir::Expr>>( - &mut self, - expr: &hir::Expr, - pred: CFGIndex, - subexprs: I, - ) -> CFGIndex { - let subexprs_exit = self.exprs(subexprs, pred); - self.add_ast_node(expr.hir_id.local_id, &[subexprs_exit]) - } - - fn match_(&mut self, id: hir::ItemLocalId, discr: &hir::Expr, - arms: &[hir::Arm], pred: CFGIndex) -> CFGIndex { - // The CFG for match expressions is quite complex, so no ASCII - // art for it (yet). - // - // The CFG generated below matches roughly what MIR contains. - // Each pattern and guard is visited in parallel, with - // arms containing multiple patterns generating multiple nodes - // for the same guard expression. The guard expressions chain - // into each other from top to bottom, with a specific - // exception to allow some additional valid programs - // (explained below). MIR differs slightly in that the - // pattern matching may continue after a guard but the visible - // behaviour should be the same. - // - // What is going on is explained in further comments. - - // Visit the discriminant expression. - let discr_exit = self.expr(discr, pred); - - // Add a node for the exit of the match expression as a whole. - let expr_exit = self.add_ast_node(id, &[]); - - // Keep track of the previous guard expressions. - let mut prev_guard = None; - let match_scope = region::Scope { id, data: region::ScopeData::Node }; - - for arm in arms { - // Add an exit node for when we've visited all the - // patterns and the guard (if there is one) in the arm. - let bindings_exit = self.add_dummy_node(&[]); - - for pat in &arm.pats { - // Visit the pattern, coming from the discriminant exit - let mut pat_exit = self.pat(&pat, discr_exit); - - // If there is a guard expression, handle it here. - if let Some(ref guard) = arm.guard { - // Add a dummy node for the previous guard - // expression to target. - let guard_start = self.add_dummy_node(&[pat_exit]); - // Visit the guard expression. - let guard_exit = match guard { - hir::Guard::If(ref e) => (&**e, self.expr(e, guard_start)), - }; - // #47295: We used to have very special case code - // here for when a pair of arms are both formed - // solely from constants, and if so, not add these - // edges. But this was not actually sound without - // other constraints that we stopped enforcing at - // some point. - if let Some((prev_guard, prev_index)) = prev_guard.take() { - self.add_exiting_edge(prev_guard, prev_index, match_scope, guard_start); - } - - // Push the guard onto the list of previous guards. - prev_guard = Some(guard_exit); - - // Update the exit node for the pattern. - pat_exit = guard_exit.1; - } - - // Add an edge from the exit of this pattern to the exit of the arm. - self.add_contained_edge(pat_exit, bindings_exit); - } - - // Visit the body of this arm. - let body_exit = self.expr(&arm.body, bindings_exit); - - let arm_exit = self.add_ast_node(arm.hir_id.local_id, &[body_exit]); - - // Link the body to the exit of the expression. - self.add_contained_edge(arm_exit, expr_exit); - } - - expr_exit - } - - fn add_dummy_node(&mut self, preds: &[CFGIndex]) -> CFGIndex { - self.add_node(CFGNodeData::Dummy, preds) - } - - fn add_ast_node(&mut self, id: hir::ItemLocalId, preds: &[CFGIndex]) -> CFGIndex { - self.add_node(CFGNodeData::AST(id), preds) - } - - fn add_unreachable_node(&mut self) -> CFGIndex { - self.add_node(CFGNodeData::Unreachable, &[]) - } - - fn add_node(&mut self, data: CFGNodeData, preds: &[CFGIndex]) -> CFGIndex { - let node = self.graph.add_node(data); - for &pred in preds { - self.add_contained_edge(pred, node); - } - node - } - - fn add_contained_edge( - &mut self, - source: CFGIndex, - target: CFGIndex, - ) { - let data = CFGEdgeData {exiting_scopes: vec![] }; - self.graph.add_edge(source, target, data); - } - - fn add_exiting_edge( - &mut self, - from_expr: &hir::Expr, - from_index: CFGIndex, - target_scope: region::Scope, - to_index: CFGIndex, - ) { - let mut data = CFGEdgeData { exiting_scopes: vec![] }; - let mut scope = region::Scope { - id: from_expr.hir_id.local_id, - data: region::ScopeData::Node - }; - let region_scope_tree = self.tcx.region_scope_tree(self.owner_def_id); - while scope != target_scope { - data.exiting_scopes.push(scope.item_local_id()); - scope = region_scope_tree.encl_scope(scope); - } - self.graph.add_edge(from_index, to_index, data); - } - - fn add_returning_edge( - &mut self, - _from_expr: &hir::Expr, - from_index: CFGIndex, - ) { - let data = CFGEdgeData { - exiting_scopes: self.loop_scopes.iter() - .rev() - .map(|&LoopScope { loop_id: id, .. }| id) - .collect() - }; - self.graph.add_edge(from_index, self.fn_exit, data); - } - - fn find_scope_edge( - &self, - expr: &hir::Expr, - destination: hir::Destination, - scope_cf_kind: ScopeCfKind, - ) -> (region::Scope, CFGIndex) { - match destination.target_id { - Ok(loop_id) => { - for b in &self.breakable_block_scopes { - if b.block_expr_id == loop_id.local_id { - let scope = region::Scope { - id: loop_id.local_id, - data: region::ScopeData::Node - }; - return (scope, match scope_cf_kind { - ScopeCfKind::Break => b.break_index, - ScopeCfKind::Continue => bug!("can't continue to block"), - }); - } - } - for l in &self.loop_scopes { - if l.loop_id == loop_id.local_id { - let scope = region::Scope { - id: loop_id.local_id, - data: region::ScopeData::Node - }; - return (scope, match scope_cf_kind { - ScopeCfKind::Break => l.break_index, - ScopeCfKind::Continue => l.continue_index, - }); - } - } - span_bug!(expr.span, "no scope for ID {}", loop_id); - } - Err(err) => span_bug!(expr.span, "scope error: {}", err), - } - } -} - -#[derive(Copy, Clone, Eq, PartialEq)] -enum ScopeCfKind { - Break, - Continue, -}
diff --git a/src/librustc_ast_borrowck/cfg/graphviz.rs b/src/librustc_ast_borrowck/cfg/graphviz.rs deleted file mode 100644 index 99c6b49..0000000 --- a/src/librustc_ast_borrowck/cfg/graphviz.rs +++ /dev/null
@@ -1,119 +0,0 @@ -/// This module provides linkage between `rustc::middle::graph` and -/// libgraphviz traits. - -use crate::cfg; -use rustc::hir; -use rustc::ty::TyCtxt; - -pub(crate) type Node<'a> = (cfg::CFGIndex, &'a cfg::CFGNode); -pub(crate) type Edge<'a> = &'a cfg::CFGEdge; - -pub struct LabelledCFG<'a, 'tcx> { - pub tcx: TyCtxt<'tcx>, - pub cfg: &'a cfg::CFG, - pub name: String, - /// `labelled_edges` controls whether we emit labels on the edges. - pub labelled_edges: bool, -} - -impl<'a, 'tcx> LabelledCFG<'a, 'tcx> { - fn local_id_to_string(&self, local_id: hir::ItemLocalId) -> String { - assert!(self.cfg.owner_def_id.is_local()); - let hir_id = hir::HirId { - owner: self.tcx.hir().def_index_to_hir_id(self.cfg.owner_def_id.index).owner, - local_id - }; - let s = self.tcx.hir().node_to_string(hir_id); - - // Replacing newlines with `\\l` causes each line to be left-aligned, - // improving presentation of (long) pretty-printed expressions. - if s.contains("\n") { - let mut s = s.replace("\n", "\\l"); - // Apparently left-alignment applies to the line that precedes - // `\l`, not the line that follows; so, add `\l` at end of string - // if not already present, ensuring last line gets left-aligned - // as well. - let mut last_two: Vec<_> = - s.chars().rev().take(2).collect(); - last_two.reverse(); - if last_two != ['\\', 'l'] { - s.push_str("\\l"); - } - s - } else { - s - } - } -} - -impl<'a, 'hir> dot::Labeller<'a> for LabelledCFG<'a, 'hir> { - type Node = Node<'a>; - type Edge = Edge<'a>; - fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new(&self.name[..]).unwrap() } - - fn node_id(&'a self, &(i,_): &Node<'a>) -> dot::Id<'a> { - dot::Id::new(format!("N{}", i.node_id())).unwrap() - } - - fn node_label(&'a self, &(i, n): &Node<'a>) -> dot::LabelText<'a> { - if i == self.cfg.entry { - dot::LabelText::LabelStr("entry".into()) - } else if i == self.cfg.exit { - dot::LabelText::LabelStr("exit".into()) - } else if n.data.id() == hir::DUMMY_ITEM_LOCAL_ID { - dot::LabelText::LabelStr("(dummy_node)".into()) - } else { - let s = self.local_id_to_string(n.data.id()); - dot::LabelText::EscStr(s.into()) - } - } - - fn edge_label(&self, e: &Edge<'a>) -> dot::LabelText<'a> { - let mut label = String::new(); - if !self.labelled_edges { - return dot::LabelText::EscStr(label.into()); - } - let mut put_one = false; - for (i, &id) in e.data.exiting_scopes.iter().enumerate() { - if put_one { - label.push_str(",\\l"); - } else { - put_one = true; - } - let s = self.local_id_to_string(id); - label.push_str(&format!("exiting scope_{} {}", - i, - &s[..])); - } - dot::LabelText::EscStr(label.into()) - } -} - -impl<'a> dot::GraphWalk<'a> for &'a cfg::CFG { - type Node = Node<'a>; - type Edge = Edge<'a>; - fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { - let v: Vec<_> = self.graph.enumerated_nodes().collect(); - v.into() - } - fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { - self.graph.all_edges().iter().collect() - } - fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { - let i = edge.source(); - (i, self.graph.node(i)) - } - fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { - let i = edge.target(); - (i, self.graph.node(i)) - } -} - -impl<'a, 'hir> dot::GraphWalk<'a> for LabelledCFG<'a, 'hir> { - type Node = Node<'a>; - type Edge = Edge<'a>; - fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { self.cfg.nodes() } - fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.cfg.edges() } - fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) } - fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) } -}
diff --git a/src/librustc_ast_borrowck/cfg/mod.rs b/src/librustc_ast_borrowck/cfg/mod.rs deleted file mode 100644 index 981199c..0000000 --- a/src/librustc_ast_borrowck/cfg/mod.rs +++ /dev/null
@@ -1,55 +0,0 @@ -//! Module that constructs a control-flow graph representing an item. -//! Uses `Graph` as the underlying representation. - -use rustc_data_structures::graph::implementation as graph; -use rustc::ty::TyCtxt; -use rustc::hir; -use rustc::hir::def_id::DefId; - -mod construct; -pub mod graphviz; - -pub struct CFG { - owner_def_id: DefId, - pub(crate) graph: CFGGraph, - pub(crate) entry: CFGIndex, - exit: CFGIndex, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum CFGNodeData { - AST(hir::ItemLocalId), - Entry, - Exit, - Dummy, - Unreachable, -} - -impl CFGNodeData { - pub(crate) fn id(&self) -> hir::ItemLocalId { - if let CFGNodeData::AST(id) = *self { - id - } else { - hir::DUMMY_ITEM_LOCAL_ID - } - } -} - -#[derive(Debug)] -pub struct CFGEdgeData { - pub(crate) exiting_scopes: Vec<hir::ItemLocalId> -} - -pub(crate) type CFGIndex = graph::NodeIndex; - -pub(crate) type CFGGraph = graph::Graph<CFGNodeData, CFGEdgeData>; - -pub(crate) type CFGNode = graph::Node<CFGNodeData>; - -pub(crate) type CFGEdge = graph::Edge<CFGEdgeData>; - -impl CFG { - pub fn new(tcx: TyCtxt<'_>, body: &hir::Body) -> CFG { - construct::construct(tcx, body) - } -}
diff --git a/src/librustc_ast_borrowck/dataflow.rs b/src/librustc_ast_borrowck/dataflow.rs deleted file mode 100644 index a856290..0000000 --- a/src/librustc_ast_borrowck/dataflow.rs +++ /dev/null
@@ -1,672 +0,0 @@ -//! A module for propagating forward dataflow information. The analysis -//! assumes that the items to be propagated can be represented as bits -//! and thus uses bitvectors. Your job is simply to specify the so-called -//! GEN and KILL bits for each expression. - -use crate::cfg::{self, CFGIndex}; -use std::mem; -use std::usize; -use log::debug; - -use rustc_data_structures::graph::implementation::OUTGOING; - -use rustc::util::nodemap::FxHashMap; -use rustc::hir; -use rustc::hir::intravisit; -use rustc::hir::print as pprust; -use rustc::ty::TyCtxt; - -#[derive(Copy, Clone, Debug)] -pub enum EntryOrExit { - Entry, - Exit, -} - -#[derive(Clone)] -pub struct DataFlowContext<'tcx, O> { - tcx: TyCtxt<'tcx>, - - /// a name for the analysis using this dataflow instance - analysis_name: &'static str, - - /// the data flow operator - oper: O, - - /// number of bits to propagate per id - bits_per_id: usize, - - /// number of words we will use to store bits_per_id. - /// equal to bits_per_id/usize::BITS rounded up. - words_per_id: usize, - - // mapping from node to cfg node index - // FIXME (#6298): Shouldn't this go with CFG? - local_id_to_index: FxHashMap<hir::ItemLocalId, Vec<CFGIndex>>, - - // Bit sets per cfg node. The following three fields (`gens`, `kills`, - // and `on_entry`) all have the same structure. For each id in - // `id_range`, there is a range of words equal to `words_per_id`. - // So, to access the bits for any given id, you take a slice of - // the full vector (see the method `compute_id_range()`). - /// bits generated as we exit the cfg node. Updated by `add_gen()`. - gens: Vec<usize>, - - /// bits killed as we exit the cfg node, or non-locally jump over - /// it. Updated by `add_kill(KillFrom::ScopeEnd)`. - scope_kills: Vec<usize>, - - /// bits killed as we exit the cfg node directly; if it is jumped - /// over, e.g., via `break`, the kills are not reflected in the - /// jump's effects. Updated by `add_kill(KillFrom::Execution)`. - action_kills: Vec<usize>, - - /// bits that are valid on entry to the cfg node. Updated by - /// `propagate()`. - on_entry: Vec<usize>, -} - -pub trait BitwiseOperator { - /// Joins two predecessor bits together, typically either `|` or `&` - fn join(&self, succ: usize, pred: usize) -> usize; -} - -/// Parameterization for the precise form of data flow that is used. -pub trait DataFlowOperator : BitwiseOperator { - /// Specifies the initial value for each bit in the `on_entry` set - fn initial_value(&self) -> bool; -} - -struct PropagationContext<'a, 'tcx, O> { - dfcx: &'a mut DataFlowContext<'tcx, O>, - changed: bool, -} - -fn get_cfg_indices(id: hir::ItemLocalId, - index: &FxHashMap<hir::ItemLocalId, Vec<CFGIndex>>) - -> &[CFGIndex] { - index.get(&id).map_or(&[], |v| &v[..]) -} - -impl<'tcx, O: DataFlowOperator> DataFlowContext<'tcx, O> { - fn has_bitset_for_local_id(&self, n: hir::ItemLocalId) -> bool { - assert!(n != hir::DUMMY_ITEM_LOCAL_ID); - self.local_id_to_index.contains_key(&n) - } -} - -impl<'tcx, O: DataFlowOperator> pprust::PpAnn for DataFlowContext<'tcx, O> { - fn nested(&self, state: &mut pprust::State<'_>, nested: pprust::Nested) { - pprust::PpAnn::nested(self.tcx.hir(), state, nested) - } - fn pre(&self, - ps: &mut pprust::State<'_>, - node: pprust::AnnNode<'_>) { - let id = match node { - pprust::AnnNode::Name(_) => return, - pprust::AnnNode::Expr(expr) => expr.hir_id.local_id, - pprust::AnnNode::Block(blk) => blk.hir_id.local_id, - pprust::AnnNode::Item(_) | - pprust::AnnNode::SubItem(_) => return, - pprust::AnnNode::Pat(pat) => pat.hir_id.local_id, - pprust::AnnNode::Arm(arm) => arm.hir_id.local_id, - }; - - if !self.has_bitset_for_local_id(id) { - return; - } - - assert!(self.bits_per_id > 0); - let indices = get_cfg_indices(id, &self.local_id_to_index); - for &cfgidx in indices { - let (start, end) = self.compute_id_range(cfgidx); - let on_entry = &self.on_entry[start.. end]; - let entry_str = bits_to_string(on_entry); - - let gens = &self.gens[start.. end]; - let gens_str = if gens.iter().any(|&u| u != 0) { - format!(" gen: {}", bits_to_string(gens)) - } else { - String::new() - }; - - let action_kills = &self.action_kills[start .. end]; - let action_kills_str = if action_kills.iter().any(|&u| u != 0) { - format!(" action_kill: {}", bits_to_string(action_kills)) - } else { - String::new() - }; - - let scope_kills = &self.scope_kills[start .. end]; - let scope_kills_str = if scope_kills.iter().any(|&u| u != 0) { - format!(" scope_kill: {}", bits_to_string(scope_kills)) - } else { - String::new() - }; - - ps.synth_comment( - format!("id {}: {}{}{}{}", id.as_usize(), entry_str, - gens_str, action_kills_str, scope_kills_str)); - ps.s.space(); - } - } -} - -fn build_local_id_to_index(body: Option<&hir::Body>, - cfg: &cfg::CFG) - -> FxHashMap<hir::ItemLocalId, Vec<CFGIndex>> { - let mut index = FxHashMap::default(); - - // FIXME(#15020) Would it be better to fold formals from decl - // into cfg itself? i.e., introduce a fn-based flow-graph in - // addition to the current block-based flow-graph, rather than - // have to put traversals like this here? - if let Some(body) = body { - add_entries_from_fn_body(&mut index, body, cfg.entry); - } - - cfg.graph.each_node(|node_idx, node| { - if let cfg::CFGNodeData::AST(id) = node.data { - index.entry(id).or_default().push(node_idx); - } - true - }); - - return index; - - /// Adds mappings from the ast nodes for the formal bindings to - /// the entry-node in the graph. - fn add_entries_from_fn_body(index: &mut FxHashMap<hir::ItemLocalId, Vec<CFGIndex>>, - body: &hir::Body, - entry: CFGIndex) { - use rustc::hir::intravisit::Visitor; - - struct Formals<'a> { - entry: CFGIndex, - index: &'a mut FxHashMap<hir::ItemLocalId, Vec<CFGIndex>>, - } - let mut formals = Formals { entry: entry, index: index }; - for param in &body.params { - formals.visit_pat(¶m.pat); - } - impl<'a, 'v> Visitor<'v> for Formals<'a> { - fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'v> { - intravisit::NestedVisitorMap::None - } - - fn visit_pat(&mut self, p: &hir::Pat) { - self.index.entry(p.hir_id.local_id).or_default().push(self.entry); - intravisit::walk_pat(self, p) - } - } - } -} - -/// Flag used by `add_kill` to indicate whether the provided kill -/// takes effect only when control flows directly through the node in -/// question, or if the kill's effect is associated with any -/// control-flow directly through or indirectly over the node. -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum KillFrom { - /// A `ScopeEnd` kill is one that takes effect when any control - /// flow goes over the node. A kill associated with the end of the - /// scope of a variable declaration `let x;` is an example of a - /// `ScopeEnd` kill. - ScopeEnd, - - /// An `Execution` kill is one that takes effect only when control - /// flow goes through the node to completion. A kill associated - /// with an assignment statement `x = expr;` is an example of an - /// `Execution` kill. - Execution, -} - -impl<'tcx, O: DataFlowOperator> DataFlowContext<'tcx, O> { - pub fn new( - tcx: TyCtxt<'tcx>, - analysis_name: &'static str, - body: Option<&hir::Body>, - cfg: &cfg::CFG, - oper: O, - bits_per_id: usize, - ) -> DataFlowContext<'tcx, O> { - let usize_bits = mem::size_of::<usize>() * 8; - let words_per_id = (bits_per_id + usize_bits - 1) / usize_bits; - let num_nodes = cfg.graph.all_nodes().len(); - - debug!("DataFlowContext::new(analysis_name: {}, \ - bits_per_id={}, words_per_id={}) \ - num_nodes: {}", - analysis_name, bits_per_id, words_per_id, - num_nodes); - - let entry = if oper.initial_value() { usize::MAX } else {0}; - - let zeroes = vec![0; num_nodes * words_per_id]; - let gens = zeroes.clone(); - let kills1 = zeroes.clone(); - let kills2 = zeroes; - let on_entry = vec![entry; num_nodes * words_per_id]; - - let local_id_to_index = build_local_id_to_index(body, cfg); - - DataFlowContext { - tcx, - analysis_name, - words_per_id, - local_id_to_index, - bits_per_id, - oper, - gens, - action_kills: kills1, - scope_kills: kills2, - on_entry, - } - } - - pub fn add_gen(&mut self, id: hir::ItemLocalId, bit: usize) { - //! Indicates that `id` generates `bit` - debug!("{} add_gen(id={:?}, bit={})", - self.analysis_name, id, bit); - assert!(self.local_id_to_index.contains_key(&id)); - assert!(self.bits_per_id > 0); - - let indices = get_cfg_indices(id, &self.local_id_to_index); - for &cfgidx in indices { - let (start, end) = self.compute_id_range(cfgidx); - let gens = &mut self.gens[start.. end]; - set_bit(gens, bit); - } - } - - pub fn add_kill(&mut self, kind: KillFrom, id: hir::ItemLocalId, bit: usize) { - //! Indicates that `id` kills `bit` - debug!("{} add_kill(id={:?}, bit={})", - self.analysis_name, id, bit); - assert!(self.local_id_to_index.contains_key(&id)); - assert!(self.bits_per_id > 0); - - let indices = get_cfg_indices(id, &self.local_id_to_index); - for &cfgidx in indices { - let (start, end) = self.compute_id_range(cfgidx); - let kills = match kind { - KillFrom::Execution => &mut self.action_kills[start.. end], - KillFrom::ScopeEnd => &mut self.scope_kills[start.. end], - }; - set_bit(kills, bit); - } - } - - fn apply_gen_kill(&self, cfgidx: CFGIndex, bits: &mut [usize]) { - //! Applies the gen and kill sets for `cfgidx` to `bits` - debug!("{} apply_gen_kill(cfgidx={:?}, bits={}) [before]", - self.analysis_name, cfgidx, mut_bits_to_string(bits)); - assert!(self.bits_per_id > 0); - - let (start, end) = self.compute_id_range(cfgidx); - let gens = &self.gens[start.. end]; - bitwise(bits, gens, &Union); - let kills = &self.action_kills[start.. end]; - bitwise(bits, kills, &Subtract); - let kills = &self.scope_kills[start.. end]; - bitwise(bits, kills, &Subtract); - - debug!("{} apply_gen_kill(cfgidx={:?}, bits={}) [after]", - self.analysis_name, cfgidx, mut_bits_to_string(bits)); - } - - fn compute_id_range(&self, cfgidx: CFGIndex) -> (usize, usize) { - let n = cfgidx.node_id(); - let start = n * self.words_per_id; - let end = start + self.words_per_id; - - assert!(start < self.gens.len()); - assert!(end <= self.gens.len()); - assert!(self.gens.len() == self.action_kills.len()); - assert!(self.gens.len() == self.scope_kills.len()); - assert!(self.gens.len() == self.on_entry.len()); - - (start, end) - } - - - pub fn each_bit_on_entry<F>(&self, id: hir::ItemLocalId, mut f: F) -> bool where - F: FnMut(usize) -> bool, - { - //! Iterates through each bit that is set on entry to `id`. - //! Only useful after `propagate()` has been called. - if !self.has_bitset_for_local_id(id) { - return true; - } - let indices = get_cfg_indices(id, &self.local_id_to_index); - for &cfgidx in indices { - if !self.each_bit_for_node(EntryOrExit::Entry, cfgidx, |i| f(i)) { - return false; - } - } - return true; - } - - pub fn each_bit_for_node<F>(&self, e: EntryOrExit, cfgidx: CFGIndex, f: F) -> bool where - F: FnMut(usize) -> bool, - { - //! Iterates through each bit that is set on entry/exit to `cfgidx`. - //! Only useful after `propagate()` has been called. - - if self.bits_per_id == 0 { - // Skip the surprisingly common degenerate case. (Note - // compute_id_range requires self.words_per_id > 0.) - return true; - } - - let (start, end) = self.compute_id_range(cfgidx); - let on_entry = &self.on_entry[start.. end]; - let temp_bits; - let slice = match e { - EntryOrExit::Entry => on_entry, - EntryOrExit::Exit => { - let mut t = on_entry.to_vec(); - self.apply_gen_kill(cfgidx, &mut t); - temp_bits = t; - &temp_bits[..] - } - }; - debug!("{} each_bit_for_node({:?}, cfgidx={:?}) bits={}", - self.analysis_name, e, cfgidx, bits_to_string(slice)); - self.each_bit(slice, f) - } - - pub fn each_gen_bit<F>(&self, id: hir::ItemLocalId, mut f: F) -> bool where - F: FnMut(usize) -> bool, - { - //! Iterates through each bit in the gen set for `id`. - if !self.has_bitset_for_local_id(id) { - return true; - } - - if self.bits_per_id == 0 { - // Skip the surprisingly common degenerate case. (Note - // compute_id_range requires self.words_per_id > 0.) - return true; - } - - let indices = get_cfg_indices(id, &self.local_id_to_index); - for &cfgidx in indices { - let (start, end) = self.compute_id_range(cfgidx); - let gens = &self.gens[start.. end]; - debug!("{} each_gen_bit(id={:?}, gens={})", - self.analysis_name, id, bits_to_string(gens)); - if !self.each_bit(gens, |i| f(i)) { - return false; - } - } - return true; - } - - fn each_bit<F>(&self, words: &[usize], mut f: F) -> bool where - F: FnMut(usize) -> bool, - { - //! Helper for iterating over the bits in a bit set. - //! Returns false on the first call to `f` that returns false; - //! if all calls to `f` return true, then returns true. - - let usize_bits = mem::size_of::<usize>() * 8; - for (word_index, &word) in words.iter().enumerate() { - if word != 0 { - let base_index = word_index * usize_bits; - for offset in 0..usize_bits { - let bit = 1 << offset; - if (word & bit) != 0 { - // N.B., we round up the total number of bits - // that we store in any given bit set so that - // it is an even multiple of usize::BITS. This - // means that there may be some stray bits at - // the end that do not correspond to any - // actual value. So before we callback, check - // whether the bit_index is greater than the - // actual value the user specified and stop - // iterating if so. - let bit_index = base_index + offset as usize; - if bit_index >= self.bits_per_id { - return true; - } else if !f(bit_index) { - return false; - } - } - } - } - } - return true; - } - - pub fn add_kills_from_flow_exits(&mut self, cfg: &cfg::CFG) { - //! Whenever you have a `break` or `continue` statement, flow - //! exits through any number of enclosing scopes on its way to - //! the new destination. This function infers the kill bits of - //! those control operators based on the kill bits associated - //! with those scopes. - //! - //! This is usually called (if it is called at all), after - //! all add_gen and add_kill calls, but before propagate. - - debug!("{} add_kills_from_flow_exits", self.analysis_name); - if self.bits_per_id == 0 { - // Skip the surprisingly common degenerate case. (Note - // compute_id_range requires self.words_per_id > 0.) - return; - } - cfg.graph.each_edge(|_edge_index, edge| { - let flow_exit = edge.source(); - let (start, end) = self.compute_id_range(flow_exit); - let mut orig_kills = self.scope_kills[start.. end].to_vec(); - - let mut changed = false; - for &id in &edge.data.exiting_scopes { - let opt_cfg_idx = self.local_id_to_index.get(&id); - match opt_cfg_idx { - Some(indices) => { - for &cfg_idx in indices { - let (start, end) = self.compute_id_range(cfg_idx); - let kills = &self.scope_kills[start.. end]; - if bitwise(&mut orig_kills, kills, &Union) { - debug!("scope exits: scope id={:?} \ - (node={:?} of {:?}) added killset: {}", - id, cfg_idx, indices, - bits_to_string(kills)); - changed = true; - } - } - } - None => { - debug!("{} add_kills_from_flow_exits flow_exit={:?} \ - no cfg_idx for exiting_scope={:?}", - self.analysis_name, flow_exit, id); - } - } - } - - if changed { - let bits = &mut self.scope_kills[start.. end]; - debug!("{} add_kills_from_flow_exits flow_exit={:?} bits={} [before]", - self.analysis_name, flow_exit, mut_bits_to_string(bits)); - bits.copy_from_slice(&orig_kills[..]); - debug!("{} add_kills_from_flow_exits flow_exit={:?} bits={} [after]", - self.analysis_name, flow_exit, mut_bits_to_string(bits)); - } - true - }); - } -} - -// N.B. `Clone + 'static` only needed for pretty printing. -impl<'tcx, O: DataFlowOperator + Clone + 'static> DataFlowContext<'tcx, O> { - pub fn propagate(&mut self, cfg: &cfg::CFG, body: &hir::Body) { - //! Performs the data flow analysis. - - if self.bits_per_id == 0 { - // Optimize the surprisingly common degenerate case. - return; - } - - { - let words_per_id = self.words_per_id; - let mut propcx = PropagationContext { - dfcx: &mut *self, - changed: true - }; - - let nodes_po = cfg.graph.nodes_in_postorder(OUTGOING, cfg.entry); - let mut temp = vec![0; words_per_id]; - let mut num_passes = 0; - while propcx.changed { - num_passes += 1; - propcx.changed = false; - propcx.reset(&mut temp); - propcx.walk_cfg(cfg, &nodes_po, &mut temp); - } - debug!("finished in {} iterations", num_passes); - } - - debug!("Dataflow result for {}:", self.analysis_name); - debug!("{}", pprust::to_string(self, |s| { - s.cbox(pprust::INDENT_UNIT); - s.ibox(0); - s.print_expr(&body.value) - })); - } -} - -impl<O: DataFlowOperator> PropagationContext<'_, 'tcx, O> { - fn walk_cfg(&mut self, - cfg: &cfg::CFG, - nodes_po: &[CFGIndex], - in_out: &mut [usize]) { - debug!("DataFlowContext::walk_cfg(in_out={}) {}", - bits_to_string(in_out), self.dfcx.analysis_name); - assert!(self.dfcx.bits_per_id > 0); - - // Iterate over nodes in reverse post-order. - for &node_index in nodes_po.iter().rev() { - let node = cfg.graph.node(node_index); - debug!("DataFlowContext::walk_cfg idx={:?} id={:?} begin in_out={}", - node_index, node.data.id(), bits_to_string(in_out)); - - let (start, end) = self.dfcx.compute_id_range(node_index); - - // Initialize local bitvector with state on-entry. - in_out.copy_from_slice(&self.dfcx.on_entry[start.. end]); - - // Compute state on-exit by applying transfer function to - // state on-entry. - self.dfcx.apply_gen_kill(node_index, in_out); - - // Propagate state on-exit from node into its successors. - self.propagate_bits_into_graph_successors_of(in_out, cfg, node_index); - } - } - - fn reset(&mut self, bits: &mut [usize]) { - let e = if self.dfcx.oper.initial_value() {usize::MAX} else {0}; - for b in bits { - *b = e; - } - } - - fn propagate_bits_into_graph_successors_of(&mut self, - pred_bits: &[usize], - cfg: &cfg::CFG, - cfgidx: CFGIndex) { - for (_, edge) in cfg.graph.outgoing_edges(cfgidx) { - self.propagate_bits_into_entry_set_for(pred_bits, edge); - } - } - - fn propagate_bits_into_entry_set_for(&mut self, - pred_bits: &[usize], - edge: &cfg::CFGEdge) { - let source = edge.source(); - let cfgidx = edge.target(); - debug!("{} propagate_bits_into_entry_set_for(pred_bits={}, {:?} to {:?})", - self.dfcx.analysis_name, bits_to_string(pred_bits), source, cfgidx); - assert!(self.dfcx.bits_per_id > 0); - - let (start, end) = self.dfcx.compute_id_range(cfgidx); - let changed = { - // (scoping mutable borrow of self.dfcx.on_entry) - let on_entry = &mut self.dfcx.on_entry[start.. end]; - bitwise(on_entry, pred_bits, &self.dfcx.oper) - }; - if changed { - debug!("{} changed entry set for {:?} to {}", - self.dfcx.analysis_name, cfgidx, - bits_to_string(&self.dfcx.on_entry[start.. end])); - self.changed = true; - } - } -} - -fn mut_bits_to_string(words: &mut [usize]) -> String { - bits_to_string(words) -} - -fn bits_to_string(words: &[usize]) -> String { - let mut result = String::new(); - let mut sep = '['; - - // Note: this is a little endian printout of bytes. - - for &word in words { - let mut v = word; - for _ in 0..mem::size_of::<usize>() { - result.push(sep); - result.push_str(&format!("{:02x}", v & 0xFF)); - v >>= 8; - sep = '-'; - } - } - result.push(']'); - return result -} - -#[inline] -fn bitwise<Op: BitwiseOperator>(out_vec: &mut [usize], - in_vec: &[usize], - op: &Op) -> bool { - assert_eq!(out_vec.len(), in_vec.len()); - let mut changed = false; - for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec) { - let old_val = *out_elt; - let new_val = op.join(old_val, *in_elt); - *out_elt = new_val; - changed |= old_val != new_val; - } - changed -} - -fn set_bit(words: &mut [usize], bit: usize) -> bool { - debug!("set_bit: words={} bit={}", - mut_bits_to_string(words), bit_str(bit)); - let usize_bits = mem::size_of::<usize>() * 8; - let word = bit / usize_bits; - let bit_in_word = bit % usize_bits; - let bit_mask = 1 << bit_in_word; - debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, bit_mask); - let oldv = words[word]; - let newv = oldv | bit_mask; - words[word] = newv; - oldv != newv -} - -fn bit_str(bit: usize) -> String { - let byte = bit >> 3; - let lobits = 1 << (bit & 0b111); - format!("[{}:{}-{:02x}]", bit, byte, lobits) -} - -struct Union; -impl BitwiseOperator for Union { - fn join(&self, a: usize, b: usize) -> usize { a | b } -} -struct Subtract; -impl BitwiseOperator for Subtract { - fn join(&self, a: usize, b: usize) -> usize { a & !b } -}
diff --git a/src/librustc_ast_borrowck/graphviz.rs b/src/librustc_ast_borrowck/graphviz.rs deleted file mode 100644 index c077dc8..0000000 --- a/src/librustc_ast_borrowck/graphviz.rs +++ /dev/null
@@ -1,145 +0,0 @@ -//! This module provides linkage between rustc::middle::graph and -//! libgraphviz traits, specialized to attaching borrowck analysis -//! data to rendered labels. - -pub use Variant::*; - -pub(crate) use crate::cfg::graphviz::{Node, Edge}; -use crate::cfg::graphviz as cfg_dot; -use crate::cfg::CFGIndex; -use crate::borrowck::{self, BorrowckCtxt, LoanPath}; -use crate::dataflow::{DataFlowOperator, DataFlowContext, EntryOrExit}; -use log::debug; -use std::rc::Rc; - -#[derive(Debug, Copy, Clone)] -pub enum Variant { - Loans, - Moves, - Assigns, -} - -impl Variant { - pub fn short_name(&self) -> &'static str { - match *self { - Loans => "loans", - Moves => "moves", - Assigns => "assigns", - } - } -} - -pub struct DataflowLabeller<'a, 'tcx> { - pub inner: cfg_dot::LabelledCFG<'a, 'tcx>, - pub variants: Vec<Variant>, - pub borrowck_ctxt: &'a BorrowckCtxt<'a, 'tcx>, - pub analysis_data: &'a borrowck::AnalysisData<'tcx>, -} - -impl<'a, 'tcx> DataflowLabeller<'a, 'tcx> { - fn dataflow_for(&self, e: EntryOrExit, n: &Node<'a>) -> String { - let id = n.1.data.id(); - debug!("dataflow_for({:?}, id={:?}) {:?}", e, id, self.variants); - let mut sets = String::new(); - let mut seen_one = false; - for &variant in &self.variants { - if seen_one { sets.push_str(" "); } else { seen_one = true; } - sets.push_str(variant.short_name()); - sets.push_str(": "); - sets.push_str(&self.dataflow_for_variant(e, n, variant)); - } - sets - } - - fn dataflow_for_variant(&self, e: EntryOrExit, n: &Node<'_>, v: Variant) -> String { - let cfgidx = n.0; - match v { - Loans => self.dataflow_loans_for(e, cfgidx), - Moves => self.dataflow_moves_for(e, cfgidx), - Assigns => self.dataflow_assigns_for(e, cfgidx), - } - } - - fn build_set<O: DataFlowOperator, F>( - &self, - e: EntryOrExit, - cfgidx: CFGIndex, - dfcx: &DataFlowContext<'tcx, O>, - mut to_lp: F, - ) -> String - where - F: FnMut(usize) -> Rc<LoanPath<'tcx>>, - { - let mut saw_some = false; - let mut set = "{".to_string(); - dfcx.each_bit_for_node(e, cfgidx, |index| { - let lp = to_lp(index); - if saw_some { - set.push_str(", "); - } - let loan_str = self.borrowck_ctxt.loan_path_to_string(&lp); - set.push_str(&loan_str); - saw_some = true; - true - }); - set.push_str("}"); - set - } - - fn dataflow_loans_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String { - let dfcx = &self.analysis_data.loans; - let loan_index_to_path = |loan_index| { - let all_loans = &self.analysis_data.all_loans; - let l: &borrowck::Loan<'_> = &all_loans[loan_index]; - l.loan_path() - }; - self.build_set(e, cfgidx, dfcx, loan_index_to_path) - } - - fn dataflow_moves_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String { - let dfcx = &self.analysis_data.move_data.dfcx_moves; - let move_index_to_path = |move_index| { - let move_data = &self.analysis_data.move_data.move_data; - let moves = move_data.moves.borrow(); - let the_move: &borrowck::move_data::Move = &(*moves)[move_index]; - move_data.path_loan_path(the_move.path) - }; - self.build_set(e, cfgidx, dfcx, move_index_to_path) - } - - fn dataflow_assigns_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String { - let dfcx = &self.analysis_data.move_data.dfcx_assign; - let assign_index_to_path = |assign_index| { - let move_data = &self.analysis_data.move_data.move_data; - let assignments = move_data.var_assignments.borrow(); - let assignment: &borrowck::move_data::Assignment = &(*assignments)[assign_index]; - move_data.path_loan_path(assignment.path) - }; - self.build_set(e, cfgidx, dfcx, assign_index_to_path) - } -} - -impl<'a, 'tcx> dot::Labeller<'a> for DataflowLabeller<'a, 'tcx> { - type Node = Node<'a>; - type Edge = Edge<'a>; - fn graph_id(&'a self) -> dot::Id<'a> { self.inner.graph_id() } - fn node_id(&'a self, n: &Node<'a>) -> dot::Id<'a> { self.inner.node_id(n) } - fn node_label(&'a self, n: &Node<'a>) -> dot::LabelText<'a> { - let prefix = self.dataflow_for(EntryOrExit::Entry, n); - let suffix = self.dataflow_for(EntryOrExit::Exit, n); - let inner_label = self.inner.node_label(n); - inner_label - .prefix_line(dot::LabelText::LabelStr(prefix.into())) - .suffix_line(dot::LabelText::LabelStr(suffix.into())) - } - fn edge_label(&'a self, e: &Edge<'a>) -> dot::LabelText<'a> { self.inner.edge_label(e) } -} - -impl<'a, 'tcx> dot::GraphWalk<'a> for DataflowLabeller<'a, 'tcx> { - type Node = Node<'a>; - type Edge = Edge<'a>; - fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { self.inner.nodes() } - fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.inner.edges() } - fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { self.inner.source(edge) } - fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { self.inner.target(edge) } -}
diff --git a/src/librustc_ast_borrowck/lib.rs b/src/librustc_ast_borrowck/lib.rs deleted file mode 100644 index aea97fe..0000000 --- a/src/librustc_ast_borrowck/lib.rs +++ /dev/null
@@ -1,23 +0,0 @@ -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] - -#![allow(non_camel_case_types)] - -#![feature(in_band_lifetimes)] -#![feature(nll)] - -#![recursion_limit="256"] - -#[macro_use] -extern crate rustc; - -pub use borrowck::check_crate; -pub use borrowck::build_borrowck_dataflow_data_for_fn; - -mod borrowck; - -pub mod graphviz; - -mod dataflow; -pub mod cfg; - -pub use borrowck::provide;
diff --git a/src/librustc_codegen_llvm/Cargo.toml b/src/librustc_codegen_llvm/Cargo.toml index 98efa6a..867bbd2 100644 --- a/src/librustc_codegen_llvm/Cargo.toml +++ b/src/librustc_codegen_llvm/Cargo.toml
@@ -12,9 +12,3 @@ [dependencies] rustc_llvm = { path = "../librustc_llvm" } - -[features] -# This is used to convince Cargo to separately cache builds of `rustc_codegen_llvm` -# when this option is enabled or not. That way we can build two, cache two -# artifacts, and have nice speedy rebuilds. -emscripten = ["rustc_llvm/emscripten"]
diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs index 2ca517d..ae5cfc4 100644 --- a/src/librustc_codegen_llvm/abi.rs +++ b/src/librustc_codegen_llvm/abi.rs
@@ -264,7 +264,7 @@ val }; match self.mode { - PassMode::Ignore(_) => {} + PassMode::Ignore => {} PassMode::Pair(..) => { OperandValue::Pair(next(), next()).store(bx, dst); } @@ -319,9 +319,7 @@ ); let llreturn_ty = match self.ret.mode { - PassMode::Ignore(IgnoreMode::Zst) => cx.type_void(), - PassMode::Ignore(IgnoreMode::CVarArgs) => - bug!("`va_list` should never be a return type"), + PassMode::Ignore => cx.type_void(), PassMode::Direct(_) | PassMode::Pair(..) => { self.ret.layout.immediate_llvm_type(cx) } @@ -339,7 +337,7 @@ } let llarg_ty = match arg.mode { - PassMode::Ignore(_) => continue, + PassMode::Ignore => continue, PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx), PassMode::Pair(..) => { llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0, true)); @@ -408,7 +406,7 @@ apply(&ArgAttributes::new(), None); } match arg.mode { - PassMode::Ignore(_) => {} + PassMode::Ignore => {} PassMode::Direct(ref attrs) | PassMode::Indirect(ref attrs, None) => apply(attrs, Some(arg.layout.llvm_type(cx))), PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => { @@ -455,7 +453,7 @@ apply(&ArgAttributes::new(), None); } match arg.mode { - PassMode::Ignore(_) => {} + PassMode::Ignore => {} PassMode::Direct(ref attrs) | PassMode::Indirect(ref attrs, None) => apply(attrs, Some(arg.layout.llvm_type(bx))), PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {
diff --git a/src/librustc_codegen_llvm/allocator.rs b/src/librustc_codegen_llvm/allocator.rs index 5d43bf6..11b6e0b 100644 --- a/src/librustc_codegen_llvm/allocator.rs +++ b/src/librustc_codegen_llvm/allocator.rs
@@ -3,7 +3,7 @@ use crate::attributes; use libc::c_uint; use rustc::ty::TyCtxt; -use syntax::ext::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; +use syntax::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; use crate::ModuleLlvm; use crate::llvm::{self, False, True}; @@ -68,7 +68,7 @@ let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, - "entry\0".as_ptr() as *const _); + "entry\0".as_ptr().cast()); let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); @@ -80,7 +80,7 @@ args.as_ptr(), args.len() as c_uint, None, - "\0".as_ptr() as *const _); + "\0".as_ptr().cast()); llvm::LLVMSetTailCall(ret, True); if output.is_some() { llvm::LLVMBuildRet(llbuilder, ret);
diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index 423a2df..6a36a4a 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs
@@ -96,10 +96,12 @@ } // Currently stack probes seem somewhat incompatible with the address - // sanitizer. With asan we're already protected from stack overflow anyway - // so we don't really need stack probes regardless. - if let Some(Sanitizer::Address) = cx.sess().opts.debugging_opts.sanitizer { - return + // sanitizer and thread sanitizer. With asan we're already protected from + // stack overflow anyway so we don't really need stack probes regardless. + match cx.sess().opts.debugging_opts.sanitizer { + Some(Sanitizer::Address) | + Some(Sanitizer::Thread) => return, + _ => {}, } // probestack doesn't play nice either with `-C profile-generate`. @@ -268,23 +270,12 @@ // optimize based on this! false } else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) { - // If a specific #[unwind] attribute is present, use that + // If a specific #[unwind] attribute is present, use that. true } else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { - // Special attribute for allocator functions, which can't unwind + // Special attribute for allocator functions, which can't unwind. false - } else if let Some(_) = id { - // rust-lang/rust#64655, rust-lang/rust#63909: to minimize - // risk associated with changing cases where nounwind - // attribute is attached, this code is deliberately mimicking - // old control flow based on whether `id` is `Some` or `None`. - // - // However, in the long term we should either: - // - fold this into final else (i.e. stop inspecting `id`) - // - or, adopt Rust PR #63909. - // - // see also Rust RFC 2753. - + } else { let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); if sig.abi == Abi::Rust || sig.abi == Abi::RustCall { // Any Rust method (or `extern "Rust" fn` or `extern @@ -310,15 +301,6 @@ // In either case, we mark item as explicitly nounwind. false } - } else { - // assume this can possibly unwind, avoiding the application of a - // `nounwind` attribute below. - // - // (But: See comments in previous branch. Specifically, it is - // unclear whether there is real value in the assumption this - // can unwind. The conservatism here may just be papering over - // a real problem by making some UB a bit harder to hit.) - true }); // Always annotate functions with the target-cpu they are compiled for.
diff --git a/src/librustc_codegen_llvm/back/archive.rs b/src/librustc_codegen_llvm/back/archive.rs index 68d3f90..e169cfc 100644 --- a/src/librustc_codegen_llvm/back/archive.rs +++ b/src/librustc_codegen_llvm/back/archive.rs
@@ -9,7 +9,9 @@ use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::{self, ArchiveKind}; -use rustc_codegen_ssa::{METADATA_FILENAME, RLIB_BYTECODE_EXTENSION}; +use rustc_codegen_ssa::{ + METADATA_FILENAME, RLIB_BYTECODE_EXTENSION, looks_like_rust_object_file +}; use rustc_codegen_ssa::back::archive::{ArchiveBuilder, find_library}; use rustc::session::Session; use syntax::symbol::Symbol; @@ -141,7 +143,7 @@ } // Don't include Rust objects if LTO is enabled - if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") { + if lto && looks_like_rust_object_file(fname) { return true }
diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index a43fbb6..b3be3d0 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs
@@ -53,20 +53,20 @@ let symbol_filter = &|&(ref name, level): &(String, SymbolExportLevel)| { if level.is_below_threshold(export_threshold) { - let mut bytes = Vec::with_capacity(name.len() + 1); - bytes.extend(name.bytes()); - Some(CString::new(bytes).unwrap()) + Some(CString::new(name.as_str()).unwrap()) } else { None } }; let exported_symbols = cgcx.exported_symbols .as_ref().expect("needs exported symbols for LTO"); - let mut symbol_white_list = exported_symbols[&LOCAL_CRATE] - .iter() - .filter_map(symbol_filter) - .collect::<Vec<CString>>(); - let _timer = cgcx.profile_activity("generate_symbol_white_list_for_thinlto"); + let mut symbol_white_list = { + let _timer = cgcx.prof.generic_activity("LLVM_lto_generate_symbol_white_list"); + exported_symbols[&LOCAL_CRATE] + .iter() + .filter_map(symbol_filter) + .collect::<Vec<CString>>() + }; info!("{} symbols to preserve in this crate", symbol_white_list.len()); // If we're performing LTO for the entire crate graph, then for each of our @@ -95,14 +95,17 @@ } for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { - let _timer = cgcx.profile_activity(format!("load: {}", path.display())); let exported_symbols = cgcx.exported_symbols .as_ref().expect("needs exported symbols for LTO"); - symbol_white_list.extend( - exported_symbols[&cnum] - .iter() - .filter_map(symbol_filter)); + { + let _timer = cgcx.prof.generic_activity("LLVM_lto_generate_symbol_white_list"); + symbol_white_list.extend( + exported_symbols[&cnum] + .iter() + .filter_map(symbol_filter)); + } + let _timer = cgcx.prof.generic_activity("LLVM_lto_load_upstream_bitcode"); let archive = ArchiveRO::open(&path).expect("wanted an rlib"); let bytecodes = archive.iter().filter_map(|child| { child.ok().and_then(|c| c.name().map(|name| (name, c))) @@ -111,7 +114,7 @@ info!("adding bytecode {}", name); let bc_encoded = data.data(); - let (bc, id) = time_ext(cgcx.time_passes, None, &format!("decode {}", name), || { + let (bc, id) = time_ext(cgcx.time_passes, &format!("decode {}", name), || { match DecodedBytecode::new(bc_encoded) { Ok(b) => Ok((b.bytecode(), b.identifier().to_string())), Err(e) => Err(diag_handler.fatal(&e)), @@ -189,6 +192,7 @@ symbol_white_list: &[*const libc::c_char]) -> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError> { + let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_build_monolithic_module"); info!("going for a fat lto"); // Sort out all our lists of incoming modules into two lists. @@ -287,8 +291,9 @@ // save and persist everything with the original module. let mut linker = Linker::new(llmod); for (bc_decoded, name) in serialized_modules { + let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_link_module"); info!("linking {:?}", name); - time_ext(cgcx.time_passes, None, &format!("ll link {:?}", name), || { + time_ext(cgcx.time_passes, &format!("ll link {:?}", name), || { let data = bc_decoded.data(); linker.add(&data).map_err(|()| { let msg = format!("failed to load bc of {:?}", name); @@ -388,6 +393,7 @@ symbol_white_list: &[*const libc::c_char]) -> Result<(Vec<LtoModuleCodegen<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> { + let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis"); unsafe { info!("going for that thin, thin LTO"); @@ -538,7 +544,7 @@ llvm::LLVMRustAddAnalysisPasses(module.module_llvm.tm, pm, module.module_llvm.llmod()); if config.verify_llvm_ir { - let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); + let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr().cast()); llvm::LLVMRustAddPass(pm, pass.unwrap()); } @@ -573,16 +579,16 @@ // We always generate bitcode through ThinLTOBuffers, // which do not support anonymous globals if config.bitcode_needed() { - let pass = llvm::LLVMRustFindAndCreatePass("name-anon-globals\0".as_ptr() as *const _); + let pass = llvm::LLVMRustFindAndCreatePass("name-anon-globals\0".as_ptr().cast()); llvm::LLVMRustAddPass(pm, pass.unwrap()); } if config.verify_llvm_ir { - let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); + let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr().cast()); llvm::LLVMRustAddPass(pm, pass.unwrap()); } - time_ext(cgcx.time_passes, None, "LTO passes", || + time_ext(cgcx.time_passes, "LTO passes", || llvm::LLVMRunPassManager(pm, module.module_llvm.llmod())); llvm::LLVMDisposePassManager(pm); @@ -601,16 +607,6 @@ llvm::LLVMRustModuleBufferCreate(m) }) } - - pub fn parse<'a>( - &self, - name: &str, - cx: &'a llvm::Context, - handler: &Handler, - ) -> Result<&'a llvm::Module, FatalError> { - let name = CString::new(name).unwrap(); - parse_module(cx, &name, self.data(), handler) - } } impl ModuleBufferMethods for ModuleBuffer { @@ -723,7 +719,7 @@ // Like with "fat" LTO, get some better optimizations if landing pads // are disabled by removing all landing pads. if cgcx.no_landing_pads { - let _timer = cgcx.profile_activity("LLVM_remove_landing_pads"); + let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_remove_landing_pads"); llvm::LLVMRustMarkAllFunctionsNounwind(llmod); save_temp_bitcode(&cgcx, &module, "thin-lto-after-nounwind"); } @@ -736,26 +732,41 @@ // // You can find some more comments about these functions in the LLVM // bindings we've got (currently `PassWrapper.cpp`) - if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module"; - return Err(write::llvm_err(&diag_handler, msg)) + { + let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_rename"); + if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod) { + let msg = "failed to prepare thin LTO module"; + return Err(write::llvm_err(&diag_handler, msg)) + } + save_temp_bitcode(cgcx, &module, "thin-lto-after-rename"); } - save_temp_bitcode(cgcx, &module, "thin-lto-after-rename"); - if !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module"; - return Err(write::llvm_err(&diag_handler, msg)) + + { + let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_resolve_weak"); + if !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) { + let msg = "failed to prepare thin LTO module"; + return Err(write::llvm_err(&diag_handler, msg)) + } + save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve"); } - save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve"); - if !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module"; - return Err(write::llvm_err(&diag_handler, msg)) + + { + let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_internalize"); + if !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) { + let msg = "failed to prepare thin LTO module"; + return Err(write::llvm_err(&diag_handler, msg)) + } + save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize"); } - save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize"); - if !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module"; - return Err(write::llvm_err(&diag_handler, msg)) + + { + let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_import"); + if !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod) { + let msg = "failed to prepare thin LTO module"; + return Err(write::llvm_err(&diag_handler, msg)) + } + save_temp_bitcode(cgcx, &module, "thin-lto-after-import"); } - save_temp_bitcode(cgcx, &module, "thin-lto-after-import"); // Ok now this is a bit unfortunate. This is also something you won't // find upstream in LLVM's ThinLTO passes! This is a hack for now to @@ -786,18 +797,24 @@ // not too much) but for now at least gets LLVM to emit valid DWARF (or // so it appears). Hopefully we can remove this once upstream bugs are // fixed in LLVM. - llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1); - save_temp_bitcode(cgcx, &module, "thin-lto-after-patch"); + { + let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_patch_debuginfo"); + llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1); + save_temp_bitcode(cgcx, &module, "thin-lto-after-patch"); + } // Alright now that we've done everything related to the ThinLTO // analysis it's time to run some optimizations! Here we use the same // `run_pass_manager` as the "fat" LTO above except that we tell it to // populate a thin-specific pass manager, which presumably LLVM treats a // little differently. - info!("running thin lto passes over {}", module.name); - let config = cgcx.config(module.kind); - run_pass_manager(cgcx, &module, config, true); - save_temp_bitcode(cgcx, &module, "thin-lto-after-pm"); + { + let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_optimize"); + info!("running thin lto passes over {}", module.name); + let config = cgcx.config(module.kind); + run_pass_manager(cgcx, &module, config, true); + save_temp_bitcode(cgcx, &module, "thin-lto-after-pm"); + } } Ok(module) }
diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 253110d..52f3a1c 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs
@@ -221,8 +221,8 @@ llcx: &'a llvm::Context) -> Self { let data = Box::into_raw(Box::new((cgcx, handler))); unsafe { - llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, data as *mut _); - llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, data as *mut _); + llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, data.cast()); + llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, data.cast()); } DiagnosticHandlers { data, llcx } } @@ -306,6 +306,8 @@ config: &ModuleConfig) -> Result<(), FatalError> { + let _timer = cgcx.prof.generic_activity("LLVM_module_optimize"); + let llmod = module.module_llvm.llmod(); let llcx = &*module.module_llvm.llcx; let tm = &*module.module_llvm.tm; @@ -423,18 +425,16 @@ // Finally, run the actual optimization passes { - let _timer = cgcx.profile_activity("LLVM_function_passes"); + let _timer = cgcx.prof.generic_activity("LLVM_module_optimize_function_passes"); time_ext(config.time_passes, - None, &format!("llvm function passes [{}]", module_name.unwrap()), || { llvm::LLVMRustRunFunctionPassManager(fpm, llmod) }); } { - let _timer = cgcx.profile_activity("LLVM_module_passes"); + let _timer = cgcx.prof.generic_activity("LLVM_module_optimize_module_passes"); time_ext(config.time_passes, - None, &format!("llvm module passes [{}]", module_name.unwrap()), || { llvm::LLVMRunPassManager(mpm, llmod) @@ -454,7 +454,7 @@ config: &ModuleConfig) -> Result<CompiledModule, FatalError> { - let _timer = cgcx.profile_activity("codegen"); + let _timer = cgcx.prof.generic_activity("LLVM_module_codegen"); { let llmod = module.module_llvm.llmod(); let llcx = &*module.module_llvm.llcx; @@ -505,12 +505,12 @@ if write_bc || config.emit_bc_compressed || config.embed_bitcode { - let _timer = cgcx.profile_activity("LLVM_make_bitcode"); + let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_make_bitcode"); let thin = ThinBuffer::new(llmod); let data = thin.data(); if write_bc { - let _timer = cgcx.profile_activity("LLVM_emit_bitcode"); + let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_emit_bitcode"); if let Err(e) = fs::write(&bc_out, data) { let msg = format!("failed to write bytecode to {}: {}", bc_out.display(), e); diag_handler.err(&msg); @@ -518,12 +518,13 @@ } if config.embed_bitcode { - let _timer = cgcx.profile_activity("LLVM_embed_bitcode"); + let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_embed_bitcode"); embed_bitcode(cgcx, llcx, llmod, Some(data)); } if config.emit_bc_compressed { - let _timer = cgcx.profile_activity("LLVM_compress_bitcode"); + let _timer = + cgcx.prof.generic_activity("LLVM_module_codegen_emit_compressed_bitcode"); let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION); let data = bytecode::encode(&module.name, data); if let Err(e) = fs::write(&dst, data) { @@ -535,10 +536,10 @@ embed_bitcode(cgcx, llcx, llmod, None); } - time_ext(config.time_passes, None, &format!("codegen passes [{}]", module_name.unwrap()), + time_ext(config.time_passes, &format!("codegen passes [{}]", module_name.unwrap()), || -> Result<(), FatalError> { if config.emit_ir { - let _timer = cgcx.profile_activity("LLVM_emit_ir"); + let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_emit_ir"); let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); let out_c = path_to_c_string(&out); @@ -585,7 +586,7 @@ } if config.emit_asm || asm_to_obj { - let _timer = cgcx.profile_activity("LLVM_emit_asm"); + let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_emit_asm"); let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); // We can't use the same module for asm and binary output, because that triggers @@ -603,13 +604,13 @@ } if write_obj { - let _timer = cgcx.profile_activity("LLVM_emit_obj"); + let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_emit_obj"); with_codegen(tm, llmod, config.no_builtins, |cpm| { write_output_file(diag_handler, tm, cpm, llmod, &obj_out, llvm::FileType::ObjectFile) })?; } else if asm_to_obj { - let _timer = cgcx.profile_activity("LLVM_asm_to_obj"); + let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_asm_to_obj"); let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); run_assembler(cgcx, diag_handler, &assembly, &obj_out); @@ -669,7 +670,7 @@ let llglobal = llvm::LLVMAddGlobal( llmod, common::val_ty(llconst), - "rustc.embedded.module\0".as_ptr() as *const _, + "rustc.embedded.module\0".as_ptr().cast(), ); llvm::LLVMSetInitializer(llglobal, llconst); @@ -681,7 +682,7 @@ } else { ".llvmbc\0" }; - llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _); + llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); llvm::LLVMSetGlobalConstant(llglobal, llvm::True); @@ -689,7 +690,7 @@ let llglobal = llvm::LLVMAddGlobal( llmod, common::val_ty(llconst), - "rustc.embedded.cmdline\0".as_ptr() as *const _, + "rustc.embedded.cmdline\0".as_ptr().cast(), ); llvm::LLVMSetInitializer(llglobal, llconst); let section = if is_apple { @@ -697,7 +698,7 @@ } else { ".llvmcmd\0" }; - llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _); + llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); } @@ -839,7 +840,7 @@ for (imp_name, val) in globals { let imp = llvm::LLVMAddGlobal(llmod, i8p_ty, - imp_name.as_ptr() as *const _); + imp_name.as_ptr().cast()); llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty)); llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage); }
diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index 21c19e1..edd34b5 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs
@@ -36,7 +36,7 @@ use std::ffi::CString; use std::time::Instant; -use syntax_pos::symbol::InternedString; +use syntax_pos::symbol::Symbol; use rustc::hir::CodegenFnAttrs; use crate::value::Value; @@ -103,7 +103,12 @@ } } -pub fn compile_codegen_unit(tcx: TyCtxt<'tcx>, cgu_name: InternedString) { +pub fn compile_codegen_unit( + tcx: TyCtxt<'tcx>, + cgu_name: Symbol, + tx_to_llvm_workers: &std::sync::mpsc::Sender<Box<dyn std::any::Any + Send>>, +) { + let prof_timer = tcx.prof.generic_activity("codegen_module"); let start_time = Instant::now(); let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); @@ -115,17 +120,18 @@ dep_graph::hash_result, ); let time_to_codegen = start_time.elapsed(); + drop(prof_timer); // We assume that the cost to run LLVM on a CGU is proportional to // the time we needed for codegenning it. let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64; - submit_codegened_module_to_llvm(&LlvmCodegenBackend(()), tcx, module, cost); + submit_codegened_module_to_llvm(&LlvmCodegenBackend(()), tx_to_llvm_workers, module, cost); fn module_codegen( tcx: TyCtxt<'_>, - cgu_name: InternedString, + cgu_name: Symbol, ) -> ModuleCodegen<ModuleLlvm> { let cgu = tcx.codegen_unit(cgu_name); // Instantiate monomorphizations without filling out definitions yet...
diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index 423a01a..312c41b 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs
@@ -23,7 +23,6 @@ use std::ops::{Deref, Range}; use std::ptr; use std::iter::TrustedLen; -use syntax::symbol::Symbol; // All Builders must have an llfn associated with them #[must_use] @@ -52,6 +51,7 @@ impl BackendTypes for Builder<'_, 'll, 'tcx> { type Value = <CodegenCx<'ll, 'tcx> as BackendTypes>::Value; + type Function = <CodegenCx<'ll, 'tcx> as BackendTypes>::Function; type BasicBlock = <CodegenCx<'ll, 'tcx> as BackendTypes>::BasicBlock; type Type = <CodegenCx<'ll, 'tcx> as BackendTypes>::Type; type Funclet = <CodegenCx<'ll, 'tcx> as BackendTypes>::Funclet; @@ -324,7 +324,7 @@ use syntax::ast::UintTy::*; use rustc::ty::{Int, Uint}; - let new_sty = match ty.sty { + let new_kind = match ty.kind { Int(Isize) => Int(self.tcx.sess.target.isize_ty), Uint(Usize) => Uint(self.tcx.sess.target.usize_ty), ref t @ Uint(_) | ref t @ Int(_) => t.clone(), @@ -332,7 +332,7 @@ }; let name = match oop { - OverflowOp::Add => match new_sty { + OverflowOp::Add => match new_kind { Int(I8) => "llvm.sadd.with.overflow.i8", Int(I16) => "llvm.sadd.with.overflow.i16", Int(I32) => "llvm.sadd.with.overflow.i32", @@ -347,7 +347,7 @@ _ => unreachable!(), }, - OverflowOp::Sub => match new_sty { + OverflowOp::Sub => match new_kind { Int(I8) => "llvm.ssub.with.overflow.i8", Int(I16) => "llvm.ssub.with.overflow.i16", Int(I32) => "llvm.ssub.with.overflow.i32", @@ -362,7 +362,7 @@ _ => unreachable!(), }, - OverflowOp::Mul => match new_sty { + OverflowOp::Mul => match new_kind { Int(I8) => "llvm.smul.with.overflow.i8", Int(I16) => "llvm.smul.with.overflow.i16", Int(I32) => "llvm.smul.with.overflow.i32", @@ -1066,36 +1066,6 @@ // Forward to the `get_static` method of `CodegenCx` self.cx().get_static(def_id) } - - fn static_panic_msg( - &mut self, - msg: Option<Symbol>, - filename: Symbol, - line: Self::Value, - col: Self::Value, - kind: &str, - ) -> Self::Value { - let align = self.tcx.data_layout.aggregate_align.abi - .max(self.tcx.data_layout.i32_align.abi) - .max(self.tcx.data_layout.pointer_align.abi); - - let filename = self.const_str_slice(filename); - - let with_msg_components; - let without_msg_components; - - let components = if let Some(msg) = msg { - let msg = self.const_str_slice(msg); - with_msg_components = [msg, filename, line, col]; - &with_msg_components as &[_] - } else { - without_msg_components = [filename, line, col]; - &without_msg_components as &[_] - }; - - let struct_ = self.const_struct(&components, false); - self.static_addr_of(struct_, align, Some(kind)) - } } impl Builder<'a, 'll, 'tcx> {
diff --git a/src/librustc_codegen_llvm/callee.rs b/src/librustc_codegen_llvm/callee.rs index 35d5107..08fa23f 100644 --- a/src/librustc_codegen_llvm/callee.rs +++ b/src/librustc_codegen_llvm/callee.rs
@@ -33,7 +33,7 @@ assert!(!instance.substs.has_param_types()); let sig = instance.fn_sig(cx.tcx()); - if let Some(&llfn) = cx.instances().borrow().get(&instance) { + if let Some(&llfn) = cx.instances.borrow().get(&instance) { return llfn; }
diff --git a/src/librustc_codegen_llvm/common.rs b/src/librustc_codegen_llvm/common.rs index 6fbea96..f38f9df 100644 --- a/src/librustc_codegen_llvm/common.rs +++ b/src/librustc_codegen_llvm/common.rs
@@ -2,8 +2,7 @@ //! Code that is useful in various codegen modules. -use crate::llvm::{self, True, False, Bool, BasicBlock, OperandBundleDef}; -use crate::abi; +use crate::llvm::{self, True, False, Bool, BasicBlock, OperandBundleDef, ConstantInt}; use crate::consts; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; @@ -86,6 +85,8 @@ impl BackendTypes for CodegenCx<'ll, 'tcx> { type Value = &'ll Value; + type Function = &'ll Value; + type BasicBlock = &'ll BasicBlock; type Type = &'ll Type; type Funclet = Funclet<'ll>; @@ -94,16 +95,6 @@ } impl CodegenCx<'ll, 'tcx> { - pub fn const_fat_ptr( - &self, - ptr: &'ll Value, - meta: &'ll Value - ) -> &'ll Value { - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); - self.const_struct(&[ptr, meta], false) - } - pub fn const_array(&self, ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value { unsafe { return llvm::LLVMConstArray(ty, elts.as_ptr(), elts.len() as c_uint); @@ -148,13 +139,6 @@ } } - pub fn const_str_slice(&self, s: Symbol) -> &'ll Value { - let len = s.as_str().len(); - let cs = consts::ptrcast(self.const_cstr(s, false), - self.type_ptr_to(self.layout_of(self.tcx.mk_str()).llvm_type(self))); - self.const_fat_ptr(cs, self.const_usize(len as u64)) - } - pub fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value { unsafe { assert_eq!(idx as c_uint as u64, idx); @@ -235,6 +219,13 @@ unsafe { llvm::LLVMConstReal(t, val) } } + fn const_str(&self, s: Symbol) -> (&'ll Value, &'ll Value) { + let len = s.as_str().len(); + let cs = consts::ptrcast(self.const_cstr(s, false), + self.type_ptr_to(self.layout_of(self.tcx.mk_str()).llvm_type(self))); + (cs, self.const_usize(len as u64)) + } + fn const_struct( &self, elts: &[&'ll Value], @@ -243,33 +234,23 @@ struct_in_context(self.llcx, elts, packed) } - fn const_to_uint(&self, v: &'ll Value) -> u64 { - unsafe { + fn const_to_opt_uint(&self, v: &'ll Value) -> Option<u64> { + try_as_const_integral(v).map(|v| unsafe { llvm::LLVMConstIntGetZExtValue(v) - } - } - - fn is_const_integral(&self, v: &'ll Value) -> bool { - unsafe { - llvm::LLVMIsAConstantInt(v).is_some() - } + }) } fn const_to_opt_u128(&self, v: &'ll Value, sign_ext: bool) -> Option<u128> { - unsafe { - if self.is_const_integral(v) { - let (mut lo, mut hi) = (0u64, 0u64); - let success = llvm::LLVMRustConstInt128Get(v, sign_ext, - &mut hi, &mut lo); - if success { - Some(hi_lo_to_u128(lo, hi)) - } else { - None - } + try_as_const_integral(v).and_then(|v| unsafe { + let (mut lo, mut hi) = (0u64, 0u64); + let success = llvm::LLVMRustConstInt128Get(v, sign_ext, + &mut hi, &mut lo); + if success { + Some(hi_lo_to_u128(lo, hi)) } else { None } - } + }) } fn scalar_to_backend( @@ -305,7 +286,7 @@ } } Some(GlobalAlloc::Function(fn_instance)) => { - self.get_fn(fn_instance) + self.get_fn_addr(fn_instance) } Some(GlobalAlloc::Static(def_id)) => { assert!(self.tcx.is_static(def_id)); @@ -386,3 +367,9 @@ fn hi_lo_to_u128(lo: u64, hi: u64) -> u128 { ((hi as u128) << 64) | (lo as u128) } + +fn try_as_const_integral(v: &'ll Value) -> Option<&'ll ConstantInt> { + unsafe { + llvm::LLVMIsAConstantInt(v) + } +}
diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs index e71d1fc..fd7054a 100644 --- a/src/librustc_codegen_llvm/consts.rs +++ b/src/librustc_codegen_llvm/consts.rs
@@ -134,7 +134,7 @@ // extern "C" fn() from being non-null, so we can't just declare a // static and call it a day. Some linkages (like weak) will make it such // that the static actually has a null value. - let llty2 = if let ty::RawPtr(ref mt) = ty.sty { + let llty2 = if let ty::RawPtr(ref mt) = ty.kind { cx.layout_of(mt.ty).llvm_type(cx) } else { cx.sess().span_fatal( @@ -221,7 +221,7 @@ def_id); let ty = instance.ty(self.tcx); - let sym = self.tcx.symbol_name(instance).name.as_symbol(); + let sym = self.tcx.symbol_name(instance).name; debug!("get_static: sym={} instance={:?}", sym, instance); @@ -230,7 +230,7 @@ let llty = self.layout_of(ty).llvm_type(self); let (g, attrs) = match self.tcx.hir().get(id) { Node::Item(&hir::Item { - ref attrs, span, node: hir::ItemKind::Static(..), .. + ref attrs, span, kind: hir::ItemKind::Static(..), .. }) => { let sym_str = sym.as_str(); if self.get_declared_value(&sym_str).is_some() { @@ -249,7 +249,7 @@ } Node::ForeignItem(&hir::ForeignItem { - ref attrs, span, node: hir::ForeignItemKind::Static(..), .. + ref attrs, span, kind: hir::ForeignItemKind::Static(..), .. }) => { let fn_attrs = self.tcx.codegen_fn_attrs(def_id); (check_and_apply_linkage(&self, &fn_attrs, ty, sym, span), attrs) @@ -488,7 +488,7 @@ if let Some(section) = attrs.link_section { let section = llvm::LLVMMDStringInContext( self.llcx, - section.as_str().as_ptr() as *const _, + section.as_str().as_ptr().cast(), section.as_str().len() as c_uint, ); assert!(alloc.relocations().is_empty()); @@ -500,14 +500,14 @@ 0..alloc.len()); let alloc = llvm::LLVMMDStringInContext( self.llcx, - bytes.as_ptr() as *const _, + bytes.as_ptr().cast(), bytes.len() as c_uint, ); let data = [section, alloc]; let meta = llvm::LLVMMDNodeInContext(self.llcx, data.as_ptr(), 2); llvm::LLVMAddNamedMetadataOperand( self.llmod, - "wasm.custom_sections\0".as_ptr() as *const _, + "wasm.custom_sections\0".as_ptr().cast(), meta, ); }
diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 58ce970..2da9387 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs
@@ -20,7 +20,6 @@ use rustc::ty::{self, Ty, TyCtxt, Instance}; use rustc::util::nodemap::FxHashMap; use rustc_target::spec::{HasTargetSpec, Target}; -use rustc_codegen_ssa::callee::resolve_and_get_fn; use rustc_codegen_ssa::base::wants_msvc_seh; use crate::callee::get_fn; @@ -211,7 +210,7 @@ // If skipping the PLT is enabled, we need to add some module metadata // to ensure intrinsic calls don't use it. if !sess.needs_plt() { - let avoid_plt = "RtLibUseGOT\0".as_ptr() as *const _; + let avoid_plt = "RtLibUseGOT\0".as_ptr().cast(); llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1); } @@ -327,11 +326,11 @@ &self.vtables } - fn instances(&self) -> &RefCell<FxHashMap<Instance<'tcx>, &'ll Value>> { - &self.instances + fn get_fn(&self, instance: Instance<'tcx>) -> &'ll Value { + get_fn(self, instance) } - fn get_fn(&self, instance: Instance<'tcx>) -> &'ll Value { + fn get_fn_addr(&self, instance: Instance<'tcx>) -> &'ll Value { get_fn(self, instance) } @@ -362,7 +361,14 @@ let tcx = self.tcx; let llfn = match tcx.lang_items().eh_personality() { Some(def_id) if !wants_msvc_seh(self.sess()) => { - resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])) + self.get_fn_addr( + ty::Instance::resolve( + tcx, + ty::ParamEnv::reveal_all(), + def_id, + tcx.intern_substs(&[]), + ).unwrap() + ) } _ => { let name = if wants_msvc_seh(self.sess()) { @@ -390,7 +396,14 @@ let tcx = self.tcx; assert!(self.sess().target.target.options.custom_unwind_resume); if let Some(def_id) = tcx.lang_items().eh_unwind_resume() { - let llfn = resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])); + let llfn = self.get_fn_addr( + ty::Instance::resolve( + tcx, + ty::ParamEnv::reveal_all(), + def_id, + tcx.intern_substs(&[]), + ).unwrap() + ); unwresume.set(Some(llfn)); return llfn; } @@ -537,6 +550,7 @@ ifn!("llvm.trap", fn() -> void); ifn!("llvm.debugtrap", fn() -> void); ifn!("llvm.frameaddress", fn(t_i32) -> i8p); + ifn!("llvm.sideeffect", fn() -> void); ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); ifn!("llvm.powi.v2f32", fn(t_v2f32, t_i32) -> t_v2f32);
diff --git a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs index 8b3ed5b..6ee76b7 100644 --- a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs +++ b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs
@@ -1,4 +1,4 @@ -use rustc_codegen_ssa::debuginfo::{FunctionDebugContext, FunctionDebugContextData, MirDebugScope}; +use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, DebugScope}; use super::metadata::file_metadata; use super::utils::{DIB, span_start}; @@ -11,35 +11,21 @@ use syntax_pos::Pos; -use rustc_data_structures::bit_set::BitSet; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; - -use syntax_pos::BytePos; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::Idx; /// Produces DIScope DIEs for each MIR Scope which has variables defined in it. -/// If debuginfo is disabled, the returned vector is empty. -pub fn create_mir_scopes( +pub fn compute_mir_scopes( cx: &CodegenCx<'ll, '_>, mir: &Body<'_>, - debug_context: &FunctionDebugContext<&'ll DISubprogram>, -) -> IndexVec<SourceScope, MirDebugScope<&'ll DIScope>> { - let null_scope = MirDebugScope { - scope_metadata: None, - file_start_pos: BytePos(0), - file_end_pos: BytePos(0) - }; - let mut scopes = IndexVec::from_elem(null_scope, &mir.source_scopes); - - let debug_context = match *debug_context { - FunctionDebugContext::RegularContext(ref data) => data, - FunctionDebugContext::DebugInfoDisabled | - FunctionDebugContext::FunctionWithoutDebugInfo => { - return scopes; - } - }; - + fn_metadata: &'ll DISubprogram, + debug_context: &mut FunctionDebugContext<&'ll DIScope>, +) { // Find all the scopes with variables defined in them. let mut has_variables = BitSet::new_empty(mir.source_scopes.len()); + // FIXME(eddyb) base this on `decl.name`, or even better, on debuginfo. + // FIXME(eddyb) take into account that arguments always have debuginfo, + // irrespective of their name (assuming full debuginfo is enabled). for var in mir.vars_iter() { let decl = &mir.local_decls[var]; has_variables.insert(decl.visibility_scope); @@ -48,31 +34,29 @@ // Instantiate all scopes. for idx in 0..mir.source_scopes.len() { let scope = SourceScope::new(idx); - make_mir_scope(cx, &mir, &has_variables, debug_context, scope, &mut scopes); + make_mir_scope(cx, &mir, fn_metadata, &has_variables, debug_context, scope); } - - scopes } fn make_mir_scope(cx: &CodegenCx<'ll, '_>, mir: &Body<'_>, + fn_metadata: &'ll DISubprogram, has_variables: &BitSet<SourceScope>, - debug_context: &FunctionDebugContextData<&'ll DISubprogram>, - scope: SourceScope, - scopes: &mut IndexVec<SourceScope, MirDebugScope<&'ll DIScope>>) { - if scopes[scope].is_valid() { + debug_context: &mut FunctionDebugContext<&'ll DISubprogram>, + scope: SourceScope) { + if debug_context.scopes[scope].is_valid() { return; } let scope_data = &mir.source_scopes[scope]; let parent_scope = if let Some(parent) = scope_data.parent_scope { - make_mir_scope(cx, mir, has_variables, debug_context, parent, scopes); - scopes[parent] + make_mir_scope(cx, mir, fn_metadata, has_variables, debug_context, parent); + debug_context.scopes[parent] } else { // The root is the function itself. let loc = span_start(cx, mir.span); - scopes[scope] = MirDebugScope { - scope_metadata: Some(debug_context.fn_metadata), + debug_context.scopes[scope] = DebugScope { + scope_metadata: Some(fn_metadata), file_start_pos: loc.file.start_pos, file_end_pos: loc.file.end_pos, }; @@ -86,8 +70,8 @@ // However, we don't skip creating a nested scope if // our parent is the root, because we might want to // put arguments in the root and not have shadowing. - if parent_scope.scope_metadata.unwrap() != debug_context.fn_metadata { - scopes[scope] = parent_scope; + if parent_scope.scope_metadata.unwrap() != fn_metadata { + debug_context.scopes[scope] = parent_scope; return; } } @@ -105,7 +89,7 @@ loc.line as c_uint, loc.col.to_usize() as c_uint)) }; - scopes[scope] = MirDebugScope { + debug_context.scopes[scope] = DebugScope { scope_metadata, file_start_pos: loc.file.start_pos, file_end_pos: loc.file.end_pos,
diff --git a/src/librustc_codegen_llvm/debuginfo/gdb.rs b/src/librustc_codegen_llvm/debuginfo/gdb.rs index 04c9e93..9ed1c17 100644 --- a/src/librustc_codegen_llvm/debuginfo/gdb.rs +++ b/src/librustc_codegen_llvm/debuginfo/gdb.rs
@@ -37,7 +37,7 @@ let section_var = unsafe { llvm::LLVMGetNamedGlobal(cx.llmod, - c_section_var_name.as_ptr() as *const _) + c_section_var_name.as_ptr().cast()) }; section_var.unwrap_or_else(|| { @@ -52,7 +52,7 @@ llvm_type).unwrap_or_else(||{ bug!("symbol `{}` is already defined", section_var_name) }); - llvm::LLVMSetSection(section_var, section_name.as_ptr() as *const _); + llvm::LLVMSetSection(section_var, section_name.as_ptr().cast()); llvm::LLVMSetInitializer(section_var, cx.const_bytes(section_contents)); llvm::LLVMSetGlobalConstant(section_var, llvm::True); llvm::LLVMSetUnnamedAddr(section_var, llvm::True);
diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index d0b607b..f0148a2 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs
@@ -6,7 +6,7 @@ get_namespace_for_item, create_DIArray, is_node_local_to_unit}; use super::namespace::mangled_name_of_instance; use super::type_names::compute_debuginfo_type_name; -use super::{CrateDebugContext}; +use super::CrateDebugContext; use crate::abi; use crate::value::Value; use rustc_codegen_ssa::traits::*; @@ -30,7 +30,7 @@ use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; use rustc::ty::layout::{self, Align, Integer, IntegerExt, LayoutOf, PrimitiveExt, Size, TyLayout, VariantIdx}; -use rustc::ty::subst::UnpackedKind; +use rustc::ty::subst::{GenericArgKind, SubstsRef}; use rustc::session::config::{self, DebugInfo}; use rustc::util::nodemap::FxHashMap; use rustc_fs_util::path_to_c_string; @@ -46,7 +46,7 @@ use std::ptr; use std::path::{Path, PathBuf}; use syntax::ast; -use syntax::symbol::{Interner, InternedString}; +use syntax::symbol::{Interner, Symbol}; use syntax_pos::{self, Span, FileName}; impl PartialEq for llvm::Metadata { @@ -187,7 +187,7 @@ // The hasher we are using to generate the UniqueTypeId. We want // something that provides more than the 64 bits of the DefaultHasher. - let mut hasher = StableHasher::<Fingerprint>::new(); + let mut hasher = StableHasher::new(); let mut hcx = cx.tcx.create_stable_hashing_context(); let type_ = cx.tcx.erase_regions(&type_); hcx.while_hashing_spans(false, |hcx| { @@ -195,7 +195,7 @@ type_.hash_stable(hcx, &mut hasher); }); }); - let unique_type_id = hasher.finish().to_hex(); + let unique_type_id = hasher.finish::<Fingerprint>().to_hex(); let key = self.unique_id_interner.intern(&unique_type_id); self.type_to_unique_id.insert(type_, UniqueTypeId(key)); @@ -340,7 +340,7 @@ let (size, align) = cx.size_and_align_of(array_or_slice_type); - let upper_bound = match array_or_slice_type.sty { + let upper_bound = match array_or_slice_type.kind { ty::Array(_, len) => len.eval_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong, _ => -1 }; @@ -427,7 +427,7 @@ let signature_metadata: Vec<_> = iter::once( // return type - match signature.output().sty { + match signature.output().kind { ty::Tuple(ref tys) if tys.is_empty() => None, _ => Some(type_metadata(cx, signature.output(), span)) } @@ -466,7 +466,7 @@ // type is assigned the correct name, size, namespace, and source location. // However, it does not describe the trait's methods. - let containing_scope = match trait_type.sty { + let containing_scope = match trait_type.kind { ty::Dynamic(ref data, ..) => data.principal_def_id().map(|did| get_namespace_for_item(cx, did)), _ => { @@ -563,7 +563,7 @@ debug!("type_metadata: {:?}", t); let ptr_metadata = |ty: Ty<'tcx>| { - match ty.sty { + match ty.kind { ty::Slice(typ) => { Ok(vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span)) } @@ -591,7 +591,7 @@ } }; - let MetadataCreationResult { metadata, already_stored_in_typemap } = match t.sty { + let MetadataCreationResult { metadata, already_stored_in_typemap } = match t.kind { ty::Never | ty::Bool | ty::Char | @@ -682,7 +682,7 @@ } ty::Closure(def_id, substs) => { - let upvar_tys : Vec<_> = substs.upvar_tys(def_id, cx.tcx).collect(); + let upvar_tys : Vec<_> = substs.as_closure().upvar_tys(def_id, cx.tcx).collect(); let containing_scope = get_namespace_for_item(cx, def_id); prepare_tuple_metadata(cx, t, @@ -692,9 +692,10 @@ Some(containing_scope)).finalize(cx) } ty::Generator(def_id, substs, _) => { - let upvar_tys : Vec<_> = substs.prefix_tys(def_id, cx.tcx).map(|t| { - cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t) - }).collect(); + let upvar_tys : Vec<_> = substs + .as_generator().prefix_tys(def_id, cx.tcx).map(|t| { + cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t) + }).collect(); prepare_enum_metadata(cx, t, def_id, @@ -835,7 +836,7 @@ fn basic_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { debug!("basic_type_metadata: {:?}", t); - let (name, encoding) = match t.sty { + let (name, encoding) = match t.kind { ty::Never => ("!", DW_ATE_unsigned), ty::Tuple(ref elements) if elements.is_empty() => ("()", DW_ATE_unsigned), @@ -960,9 +961,9 @@ file_metadata, producer.as_ptr(), tcx.sess.opts.optimize != config::OptLevel::No, - flags.as_ptr() as *const _, + flags.as_ptr().cast(), 0, - split_name.as_ptr() as *const _, + split_name.as_ptr().cast(), kind); if tcx.sess.opts.debugging_opts.profile { @@ -991,7 +992,7 @@ if tcx.sess.opts.target_triple.triple().starts_with("wasm32") { let name_metadata = llvm::LLVMMDStringInContext( debug_context.llcontext, - rustc_producer.as_ptr() as *const _, + rustc_producer.as_ptr().cast(), rustc_producer.as_bytes().len() as c_uint, ); llvm::LLVMAddNamedMetadataOperand( @@ -1145,7 +1146,7 @@ ) -> RecursiveTypeDescription<'ll, 'tcx> { let struct_name = compute_debuginfo_type_name(cx.tcx, struct_type, false); - let (struct_def_id, variant) = match struct_type.sty { + let (struct_def_id, variant) = match struct_type.kind { ty::Adt(def, _) => (def.did, def.non_enum_variant()), _ => bug!("prepare_struct_metadata on a non-ADT") }; @@ -1268,7 +1269,7 @@ ) -> RecursiveTypeDescription<'ll, 'tcx> { let union_name = compute_debuginfo_type_name(cx.tcx, union_type, false); - let (union_def_id, variant) = match union_type.sty { + let (union_def_id, variant) = match union_type.kind { ty::Adt(def, _) => (def.did, def.non_enum_variant()), _ => bug!("prepare_union_metadata on a non-ADT") }; @@ -1334,11 +1335,11 @@ fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<MemberDescription<'ll>> { let variant_info_for = |index: VariantIdx| { - match &self.enum_type.sty { + match &self.enum_type.kind { ty::Adt(adt, _) => VariantInfo::Adt(&adt.variants[index]), ty::Generator(def_id, substs, _) => { let generator_layout = cx.tcx.generator_layout(*def_id); - VariantInfo::Generator(*substs, generator_layout, index) + VariantInfo::Generator(substs, generator_layout, index) } _ => bug!(), } @@ -1354,7 +1355,7 @@ match self.layout.variants { layout::Variants::Single { index } => { - if let ty::Adt(adt, _) = &self.enum_type.sty { + if let ty::Adt(adt, _) = &self.enum_type.kind { if adt.variants.is_empty() { return vec![]; } @@ -1611,7 +1612,7 @@ #[derive(Copy, Clone)] enum VariantInfo<'tcx> { Adt(&'tcx ty::VariantDef), - Generator(ty::GeneratorSubsts<'tcx>, &'tcx GeneratorLayout<'tcx>, VariantIdx), + Generator(SubstsRef<'tcx>, &'tcx GeneratorLayout<'tcx>, VariantIdx), } impl<'tcx> VariantInfo<'tcx> { @@ -1619,7 +1620,7 @@ match self { VariantInfo::Adt(variant) => f(&variant.ident.as_str()), VariantInfo::Generator(substs, _, variant_index) => - f(&substs.variant_name(*variant_index)), + f(&substs.as_generator().variant_name(*variant_index)), } } @@ -1747,7 +1748,7 @@ let file_metadata = unknown_file_metadata(cx); let discriminant_type_metadata = |discr: layout::Primitive| { - let enumerators_metadata: Vec<_> = match enum_type.sty { + let enumerators_metadata: Vec<_> = match enum_type.kind { ty::Adt(def, _) => def .discriminants(cx.tcx) .zip(&def.variants) @@ -1763,9 +1764,10 @@ }) .collect(), ty::Generator(_, substs, _) => substs + .as_generator() .variant_range(enum_def_id, cx.tcx) .map(|variant_index| { - let name = SmallCStr::new(&substs.variant_name(variant_index)); + let name = SmallCStr::new(&substs.as_generator().variant_name(variant_index)); unsafe { Some(llvm::LLVMRustDIBuilderCreateEnumerator( DIB(cx), @@ -1790,7 +1792,7 @@ let discriminant_base_type_metadata = type_metadata(cx, discr.to_ty(cx.tcx), syntax_pos::DUMMY_SP); - let discriminant_name = match enum_type.sty { + let discriminant_name = match enum_type.kind { ty::Adt(..) => SmallCStr::new(&cx.tcx.item_name(enum_def_id).as_str()), ty::Generator(..) => SmallCStr::new(&enum_name), _ => bug!(), @@ -1881,7 +1883,7 @@ ); } - let discriminator_name = match &enum_type.sty { + let discriminator_name = match &enum_type.kind { ty::Generator(..) => Some(SmallCStr::new(&"__state")), _ => None, }; @@ -2067,11 +2069,9 @@ { let mut composite_types_completed = debug_context(cx).composite_types_completed.borrow_mut(); - if composite_types_completed.contains(&composite_type_metadata) { + if !composite_types_completed.insert(&composite_type_metadata) { bug!("debuginfo::set_members_of_composite_type() - \ Already completed forward declaration re-encountered."); - } else { - composite_types_completed.insert(composite_type_metadata); } } @@ -2091,12 +2091,12 @@ // Compute the type parameters for a type, if any, for the given // metadata. fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> Option<&'ll DIArray> { - if let ty::Adt(def, substs) = ty.sty { + if let ty::Adt(def, substs) = ty.kind { if !substs.types().next().is_none() { let generics = cx.tcx.generics_of(def.did); let names = get_parameter_names(cx, generics); let template_params: Vec<_> = substs.iter().zip(names).filter_map(|(kind, name)| { - if let UnpackedKind::Type(ty) = kind.unpack() { + if let GenericArgKind::Type(ty) = kind.unpack() { let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); let actual_type_metadata = type_metadata(cx, actual_type, syntax_pos::DUMMY_SP); @@ -2125,7 +2125,7 @@ fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) - -> Vec<InternedString> { + -> Vec<Symbol> { let mut names = generics.parent.map_or(vec![], |def_id| { get_parameter_names(cx, cx.tcx.generics_of(def_id)) });
diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs index 6dedf10..7713fe4 100644 --- a/src/librustc_codegen_llvm/debuginfo/mod.rs +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs
@@ -1,8 +1,7 @@ // See doc.rs for documentation. mod doc; -use rustc_codegen_ssa::debuginfo::VariableAccess::*; -use rustc_codegen_ssa::debuginfo::VariableKind::*; +use rustc_codegen_ssa::mir::debuginfo::VariableKind::*; use self::utils::{DIB, span_start, create_DIArray, is_node_local_to_unit}; use self::namespace::mangled_name_of_instance; @@ -11,11 +10,11 @@ use self::source_loc::InternalDebugLocation::{self, UnknownLocation}; use crate::llvm; -use crate::llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilder, DISubprogram, DIArray, DIFlags, +use crate::llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilder, DIArray, DIFlags, DISPFlags, DILexicalBlock}; use rustc::hir::CodegenFnAttrFlags; use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; -use rustc::ty::subst::{SubstsRef, UnpackedKind}; +use rustc::ty::subst::{SubstsRef, GenericArgKind}; use crate::abi::Abi; use crate::common::CodegenCx; @@ -26,18 +25,20 @@ use rustc::session::config::{self, DebugInfo}; use rustc::util::nodemap::{DefIdMap, FxHashMap, FxHashSet}; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_data_structures::indexed_vec::IndexVec; -use rustc_codegen_ssa::debuginfo::{FunctionDebugContext, MirDebugScope, VariableAccess, - VariableKind, FunctionDebugContextData, type_names}; +use rustc_index::vec::IndexVec; +use rustc_codegen_ssa::debuginfo::type_names; +use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, DebugScope, + VariableKind}; use libc::c_uint; use std::cell::RefCell; use std::ffi::{CStr, CString}; -use syntax_pos::{self, Span, Pos}; +use smallvec::SmallVec; +use syntax_pos::{self, BytePos, Span, Pos}; use syntax::ast; -use syntax::symbol::InternedString; -use rustc::ty::layout::{self, LayoutOf, HasTyCtxt}; +use syntax::symbol::Symbol; +use rustc::ty::layout::{self, LayoutOf, HasTyCtxt, Size}; use rustc_codegen_ssa::traits::*; pub mod gdb; @@ -47,7 +48,7 @@ mod create_scope_map; mod source_loc; -pub use self::create_scope_map::{create_mir_scopes}; +pub use self::create_scope_map::compute_mir_scopes; pub use self::metadata::create_global_var_metadata; pub use self::metadata::extend_scope_to_file; pub use self::source_loc::set_source_location; @@ -127,20 +128,20 @@ if cx.sess().target.target.options.is_like_osx || cx.sess().target.target.options.is_like_android { llvm::LLVMRustAddModuleFlag(cx.llmod, - "Dwarf Version\0".as_ptr() as *const _, + "Dwarf Version\0".as_ptr().cast(), 2) } // Indicate that we want CodeView debug information on MSVC if cx.sess().target.target.options.is_like_msvc { llvm::LLVMRustAddModuleFlag(cx.llmod, - "CodeView\0".as_ptr() as *const _, + "CodeView\0".as_ptr().cast(), 1) } // Prevent bitcode readers from deleting the debug info. let ptr = "Debug Info Version\0".as_ptr(); - llvm::LLVMRustAddModuleFlag(cx.llmod, ptr as *const _, + llvm::LLVMRustAddModuleFlag(cx.llmod, ptr.cast(), llvm::LLVMRustDebugMetadataVersion()); }; } @@ -148,21 +149,23 @@ impl DebugInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { fn declare_local( &mut self, - dbg_context: &FunctionDebugContext<&'ll DISubprogram>, + dbg_context: &FunctionDebugContext<&'ll DIScope>, variable_name: ast::Name, variable_type: Ty<'tcx>, scope_metadata: &'ll DIScope, - variable_access: VariableAccess<'_, &'ll Value>, + variable_alloca: Self::Value, + direct_offset: Size, + indirect_offsets: &[Size], variable_kind: VariableKind, span: Span, ) { - assert!(!dbg_context.get_ref(span).source_locations_enabled); + assert!(!dbg_context.source_locations_enabled); let cx = self.cx(); let file = span_start(cx, span).file; let file_metadata = file_metadata(cx, &file.name, - dbg_context.get_ref(span).defining_crate); + dbg_context.defining_crate); let loc = span_start(cx, span); let type_metadata = type_metadata(cx, variable_type, span); @@ -173,49 +176,61 @@ }; let align = cx.align_of(variable_type); - let name = SmallCStr::new(&variable_name.as_str()); - match (variable_access, &[][..]) { - (DirectVariable { alloca }, address_operations) | - (IndirectVariable {alloca, address_operations}, _) => { - let metadata = unsafe { - llvm::LLVMRustDIBuilderCreateVariable( - DIB(cx), - dwarf_tag, - scope_metadata, - name.as_ptr(), - file_metadata, - loc.line as c_uint, - type_metadata, - cx.sess().opts.optimize != config::OptLevel::No, - DIFlags::FlagZero, - argument_index, - align.bytes() as u32, - ) - }; - source_loc::set_debug_location(self, - InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize())); - unsafe { - let debug_loc = llvm::LLVMGetCurrentDebugLocation(self.llbuilder); - let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd( - DIB(cx), - alloca, - metadata, - address_operations.as_ptr(), - address_operations.len() as c_uint, - debug_loc, - self.llbb()); + // Convert the direct and indirect offsets to address ops. + let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() }; + let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() }; + let mut addr_ops = SmallVec::<[_; 8]>::new(); - llvm::LLVMSetInstDebugLocation(self.llbuilder, instr); - } - source_loc::set_debug_location(self, UnknownLocation); + if direct_offset.bytes() > 0 { + addr_ops.push(op_plus_uconst()); + addr_ops.push(direct_offset.bytes() as i64); + } + for &offset in indirect_offsets { + addr_ops.push(op_deref()); + if offset.bytes() > 0 { + addr_ops.push(op_plus_uconst()); + addr_ops.push(offset.bytes() as i64); } } + + let name = SmallCStr::new(&variable_name.as_str()); + let metadata = unsafe { + llvm::LLVMRustDIBuilderCreateVariable( + DIB(cx), + dwarf_tag, + scope_metadata, + name.as_ptr(), + file_metadata, + loc.line as c_uint, + type_metadata, + cx.sess().opts.optimize != config::OptLevel::No, + DIFlags::FlagZero, + argument_index, + align.bytes() as u32, + ) + }; + source_loc::set_debug_location(self, + InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize())); + unsafe { + let debug_loc = llvm::LLVMGetCurrentDebugLocation(self.llbuilder); + let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd( + DIB(cx), + variable_alloca, + metadata, + addr_ops.as_ptr(), + addr_ops.len() as c_uint, + debug_loc, + self.llbb()); + + llvm::LLVMSetInstDebugLocation(self.llbuilder, instr); + } + source_loc::set_debug_location(self, UnknownLocation); } fn set_source_location( &mut self, - debug_context: &mut FunctionDebugContext<&'ll DISubprogram>, - scope: Option<&'ll DIScope>, + debug_context: &mut FunctionDebugContext<&'ll DIScope>, + scope: &'ll DIScope, span: Span, ) { set_source_location(debug_context, &self, scope, span) @@ -224,7 +239,7 @@ gdb::insert_reference_to_gdb_debug_scripts_section_global(self) } - fn set_var_name(&mut self, value: &'ll Value, name: impl ToString) { + fn set_var_name(&mut self, value: &'ll Value, name: &str) { // Avoid wasting time if LLVM value names aren't even enabled. if self.sess().fewer_names() { return; @@ -254,7 +269,7 @@ Err(_) => return, } - let cname = CString::new(name.to_string()).unwrap(); + let cname = SmallCStr::new(name); unsafe { llvm::LLVMSetValueName(value, cname.as_ptr()); } @@ -268,14 +283,14 @@ sig: ty::FnSig<'tcx>, llfn: &'ll Value, mir: &mir::Body<'_>, - ) -> FunctionDebugContext<&'ll DISubprogram> { + ) -> Option<FunctionDebugContext<&'ll DIScope>> { if self.sess().opts.debuginfo == DebugInfo::None { - return FunctionDebugContext::DebugInfoDisabled; + return None; } if let InstanceDef::Item(def_id) = instance.def { if self.tcx().codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_DEBUG) { - return FunctionDebugContext::FunctionWithoutDebugInfo; + return None; } } @@ -284,7 +299,7 @@ // This can be the case for functions inlined from another crate if span.is_dummy() { // FIXME(simulacrum): Probably can't happen; remove. - return FunctionDebugContext::FunctionWithoutDebugInfo; + return None; } let def_id = instance.def_id(); @@ -357,14 +372,23 @@ None) }; - // Initialize fn debug context (including scope map and namespace map) - let fn_debug_context = FunctionDebugContextData { - fn_metadata, + // Initialize fn debug context (including scopes). + // FIXME(eddyb) figure out a way to not need `Option` for `scope_metadata`. + let null_scope = DebugScope { + scope_metadata: None, + file_start_pos: BytePos(0), + file_end_pos: BytePos(0) + }; + let mut fn_debug_context = FunctionDebugContext { + scopes: IndexVec::from_elem(null_scope, &mir.source_scopes), source_locations_enabled: false, defining_crate: def_id.krate, }; - return FunctionDebugContext::RegularContext(fn_debug_context); + // Fill in all the scopes, with the information from the MIR body. + compute_mir_scopes(self, mir, fn_metadata, &mut fn_debug_context); + + return Some(fn_debug_context); fn get_function_signature<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, @@ -377,7 +401,7 @@ let mut signature = Vec::with_capacity(sig.inputs().len() + 1); // Return type -- llvm::DIBuilder wants this at index 0 - signature.push(match sig.output().sty { + signature.push(match sig.output().kind { ty::Tuple(ref tys) if tys.is_empty() => None, _ => Some(type_metadata(cx, sig.output(), syntax_pos::DUMMY_SP)) }); @@ -401,7 +425,7 @@ // This transformed type is wrong, but these function types are // already inaccurate due to ABI adjustments (see #42800). signature.extend(inputs.iter().map(|&t| { - let t = match t.sty { + let t = match t.kind { ty::Array(ct, _) if (ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() => { cx.tcx.mk_imm_ptr(ct) @@ -417,7 +441,7 @@ } if sig.abi == Abi::RustCall && !sig.inputs().is_empty() { - if let ty::Tuple(args) = sig.inputs()[sig.inputs().len() - 1].sty { + if let ty::Tuple(args) = sig.inputs()[sig.inputs().len() - 1].kind { signature.extend( args.iter().map(|argument_type| { Some(type_metadata(cx, argument_type.expect_ty(), syntax_pos::DUMMY_SP)) @@ -460,7 +484,7 @@ let template_params: Vec<_> = if cx.sess().opts.debuginfo == DebugInfo::Full { let names = get_parameter_names(cx, generics); substs.iter().zip(names).filter_map(|(kind, name)| { - if let UnpackedKind::Type(ty) = kind.unpack() { + if let GenericArgKind::Type(ty) = kind.unpack() { let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); let actual_type_metadata = @@ -490,7 +514,7 @@ fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) - -> Vec<InternedString> { + -> Vec<Symbol> { let mut names = generics.parent.map_or(vec![], |def_id| { get_parameter_names(cx, cx.tcx.generics_of(def_id)) }); @@ -516,7 +540,7 @@ // Only "class" methods are generally understood by LLVM, // so avoid methods on other types (e.g., `<*mut T>::null`). - match impl_self_ty.sty { + match impl_self_ty.kind { ty::Adt(def, ..) if !def.is_box() => { Some(type_metadata(cx, impl_self_ty, syntax_pos::DUMMY_SP)) } @@ -549,14 +573,6 @@ metadata::create_vtable_metadata(self, ty, vtable) } - fn create_mir_scopes( - &self, - mir: &mir::Body<'_>, - debug_context: &mut FunctionDebugContext<&'ll DISubprogram>, - ) -> IndexVec<mir::SourceScope, MirDebugScope<&'ll DIScope>> { - create_scope_map::create_mir_scopes(self, mir, debug_context) - } - fn extend_scope_to_file( &self, scope_metadata: &'ll DIScope, @@ -569,13 +585,4 @@ fn debuginfo_finalize(&self) { finalize(self) } - - fn debuginfo_upvar_ops_sequence(&self, byte_offset_of_var_in_env: u64) -> [i64; 4] { - unsafe { - [llvm::LLVMRustDIBuilderCreateOpDeref(), - llvm::LLVMRustDIBuilderCreateOpPlusUconst(), - byte_offset_of_var_in_env as i64, - llvm::LLVMRustDIBuilderCreateOpDeref()] - } - } }
diff --git a/src/librustc_codegen_llvm/debuginfo/namespace.rs b/src/librustc_codegen_llvm/debuginfo/namespace.rs index 8899847..628d137 100644 --- a/src/librustc_codegen_llvm/debuginfo/namespace.rs +++ b/src/librustc_codegen_llvm/debuginfo/namespace.rs
@@ -35,7 +35,7 @@ let namespace_name = match def_key.disambiguated_data.data { DefPathData::CrateRoot => cx.tcx.crate_name(def_id.krate).as_str(), - data => data.as_interned_str().as_str() + data => data.as_symbol().as_str() }; let namespace_name = SmallCStr::new(&namespace_name);
diff --git a/src/librustc_codegen_llvm/debuginfo/source_loc.rs b/src/librustc_codegen_llvm/debuginfo/source_loc.rs index dec93a6..ccb3bde 100644 --- a/src/librustc_codegen_llvm/debuginfo/source_loc.rs +++ b/src/librustc_codegen_llvm/debuginfo/source_loc.rs
@@ -2,7 +2,7 @@ use super::utils::{debug_context, span_start}; use super::metadata::UNKNOWN_COLUMN_NUMBER; -use rustc_codegen_ssa::debuginfo::FunctionDebugContext; +use rustc_codegen_ssa::mir::debuginfo::FunctionDebugContext; use crate::llvm; use crate::llvm::debuginfo::DIScope; @@ -18,22 +18,13 @@ pub fn set_source_location<D>( debug_context: &FunctionDebugContext<D>, bx: &Builder<'_, 'll, '_>, - scope: Option<&'ll DIScope>, + scope: &'ll DIScope, span: Span, ) { - let function_debug_context = match *debug_context { - FunctionDebugContext::DebugInfoDisabled => return, - FunctionDebugContext::FunctionWithoutDebugInfo => { - set_debug_location(bx, UnknownLocation); - return; - } - FunctionDebugContext::RegularContext(ref data) => data - }; - - let dbg_loc = if function_debug_context.source_locations_enabled { + let dbg_loc = if debug_context.source_locations_enabled { debug!("set_source_location: {}", bx.sess().source_map().span_to_string(span)); let loc = span_start(bx.cx(), span); - InternalDebugLocation::new(scope.unwrap(), loc.line, loc.col.to_usize()) + InternalDebugLocation::new(scope, loc.line, loc.col.to_usize()) } else { UnknownLocation };
diff --git a/src/librustc_codegen_llvm/error_codes.rs b/src/librustc_codegen_llvm/error_codes.rs deleted file mode 100644 index 042e51e..0000000 --- a/src/librustc_codegen_llvm/error_codes.rs +++ /dev/null
@@ -1,38 +0,0 @@ -register_diagnostics! { - -E0511: r##" -Invalid monomorphization of an intrinsic function was used. Erroneous code -example: - -```ignore (error-emitted-at-codegen-which-cannot-be-handled-by-compile_fail) -#![feature(platform_intrinsics)] - -extern "platform-intrinsic" { - fn simd_add<T>(a: T, b: T) -> T; -} - -fn main() { - unsafe { simd_add(0, 1); } - // error: invalid monomorphization of `simd_add` intrinsic -} -``` - -The generic type has to be a SIMD type. Example: - -``` -#![feature(repr_simd)] -#![feature(platform_intrinsics)] - -#[repr(simd)] -#[derive(Copy, Clone)] -struct i32x2(i32, i32); - -extern "platform-intrinsic" { - fn simd_add<T>(a: T, b: T) -> T; -} - -unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok! -``` -"##, - -}
diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 3f3c5ac..0242495 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs
@@ -19,10 +19,11 @@ use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; use rustc::hir; use syntax::ast::{self, FloatTy}; +use rustc_target::abi::HasDataLayout; +use rustc_codegen_ssa::common::span_invalid_monomorphization_error; use rustc_codegen_ssa::traits::*; -use rustc::session::Session; use syntax_pos::Span; use std::cmp::Ordering; @@ -91,7 +92,7 @@ let tcx = self.tcx; let callee_ty = instance.ty(tcx); - let (def_id, substs) = match callee_ty.sty { + let (def_id, substs) = match callee_ty.kind { ty::FnDef(def_id, substs) => (def_id, substs), _ => bug!("expected fn item type, found {}", callee_ty) }; @@ -694,6 +695,23 @@ return; } + "ptr_offset_from" => { + let ty = substs.type_at(0); + let pointee_size = self.size_of(ty); + + // This is the same sequence that Clang emits for pointer subtraction. + // It can be neither `nsw` nor `nuw` because the input is treated as + // unsigned but then the output is treated as signed, so neither works. + let a = args[0].immediate(); + let b = args[1].immediate(); + let a = self.ptrtoint(a, self.type_isize()); + let b = self.ptrtoint(b, self.type_isize()); + let d = self.sub(a, b); + let pointee_size = self.const_usize(pointee_size.bytes()); + // this is where the signed magic happens (notice the `s` in `exactsdiv`) + self.exactsdiv(d, pointee_size) + } + _ => bug!("unknown intrinsic '{}'", name), }; @@ -724,6 +742,13 @@ self.call(expect, &[cond, self.const_bool(expected)], None) } + fn sideeffect(&mut self) { + if self.tcx.sess.opts.debugging_opts.insert_sideeffect { + let fnname = self.get_intrinsic(&("llvm.sideeffect")); + self.call(fnname, &[], None); + } + } + fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value { let intrinsic = self.cx().get_intrinsic("llvm.va_start"); self.call(intrinsic, &[va_list], None) @@ -810,6 +835,7 @@ ) { let llfn = get_rust_try_fn(bx, &mut |mut bx| { bx.set_personality_fn(bx.eh_personality()); + bx.sideeffect(); let mut normal = bx.build_sibling_block("normal"); let mut catchswitch = bx.build_sibling_block("catchswitch"); @@ -823,7 +849,7 @@ // We're generating an IR snippet that looks like: // // declare i32 @rust_try(%func, %data, %ptr) { - // %slot = alloca i64* + // %slot = alloca [2 x i64] // invoke %func(%data) to label %normal unwind label %catchswitch // // normal: @@ -847,21 +873,25 @@ // // #include <stdint.h> // + // struct rust_panic { + // uint64_t x[2]; + // } + // // int bar(void (*foo)(void), uint64_t *ret) { // try { // foo(); // return 0; - // } catch(uint64_t a[2]) { - // ret[0] = a[0]; - // ret[1] = a[1]; + // } catch(rust_panic a) { + // ret[0] = a.x[0]; + // ret[1] = a.x[1]; // return 1; // } // } // // More information can be found in libstd's seh.rs implementation. - let i64p = bx.type_ptr_to(bx.type_i64()); - let ptr_align = bx.tcx().data_layout.pointer_align.abi; - let slot = bx.alloca(i64p, ptr_align); + let i64_2 = bx.type_array(bx.type_i64(), 2); + let i64_align = bx.tcx().data_layout.i64_align.abi; + let slot = bx.alloca(i64_2, i64_align); bx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), None); normal.ret(bx.const_i32(0)); @@ -869,22 +899,15 @@ let cs = catchswitch.catch_switch(None, None, 1); catchswitch.add_handler(cs, catchpad.llbb()); - let tydesc = match bx.tcx().lang_items().msvc_try_filter() { + let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() { Some(did) => bx.get_static(did), - None => bug!("msvc_try_filter not defined"), + None => bug!("eh_catch_typeinfo not defined, but needed for SEH unwinding"), }; let funclet = catchpad.catch_pad(cs, &[tydesc, bx.const_i32(0), slot]); - let addr = catchpad.load(slot, ptr_align); - let i64_align = bx.tcx().data_layout.i64_align.abi; - let arg1 = catchpad.load(addr, i64_align); - let val1 = bx.const_i32(1); - let gep1 = catchpad.inbounds_gep(addr, &[val1]); - let arg2 = catchpad.load(gep1, i64_align); - let local_ptr = catchpad.bitcast(local_ptr, i64p); - let gep2 = catchpad.inbounds_gep(local_ptr, &[val1]); - catchpad.store(arg1, local_ptr, i64_align); - catchpad.store(arg2, gep2, i64_align); + let payload = catchpad.load(slot, i64_align); + let local_ptr = catchpad.bitcast(local_ptr, bx.type_ptr_to(i64_2)); + catchpad.store(payload, local_ptr, i64_align); catchpad.catch_ret(&funclet, caught.llbb()); caught.ret(bx.const_i32(1)); @@ -933,6 +956,8 @@ // expected to be `*mut *mut u8` for this to actually work, but that's // managed by the standard library. + bx.sideeffect(); + let mut then = bx.build_sibling_block("then"); let mut catch = bx.build_sibling_block("catch"); @@ -950,7 +975,14 @@ // rust_try ignores the selector. let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false); let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1); - catch.add_clause(vals, bx.const_null(bx.type_i8p())); + let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() { + Some(tydesc) => { + let tydesc = bx.get_static(tydesc); + bx.bitcast(tydesc, bx.type_i8p()) + } + None => bx.const_null(bx.type_i8p()), + }; + catch.add_clause(vals, tydesc); let ptr = catch.extract_value(vals, 0); let ptr_align = bx.tcx().data_layout.pointer_align.abi; let bitcast = catch.bitcast(local_ptr, bx.type_ptr_to(bx.type_i8p())); @@ -1016,10 +1048,6 @@ rust_try } -fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) { - span_err!(a, b, E0511, "{}", c); -} - fn generic_simd_intrinsic( bx: &mut Builder<'a, 'll, 'tcx>, name: &str, @@ -1074,7 +1102,7 @@ if name == "simd_select_bitmask" { let in_ty = arg_tys[0]; - let m_len = match in_ty.sty { + let m_len = match in_ty.kind { // Note that this `.unwrap()` crashes for isize/usize, that's sort // of intentional as there's not currently a use case for that. ty::Int(i) => i.bit_width().unwrap(), @@ -1203,7 +1231,7 @@ "mismatched lengths: mask length `{}` != other vector length `{}`", m_len, v_len ); - match m_elem_ty.sty { + match m_elem_ty.kind { ty::Int(_) => {}, _ => return_error!("mask element type is `{}`, expected `i_`", m_elem_ty) } @@ -1218,12 +1246,11 @@ // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a // vector mask and returns an unsigned integer containing the most // significant bit (MSB) of each lane. - use rustc_target::abi::HasDataLayout; // If the vector has less than 8 lanes, an u8 is returned with zeroed // trailing bits. let expected_int_bits = in_len.max(8); - match ret_ty.sty { + match ret_ty.kind { ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => (), _ => return_error!( "bitmask `{}`, expected `u{}`", @@ -1232,7 +1259,7 @@ } // Integer vector <i{in_bitwidth} x in_len>: - let (i_xn, in_elem_bitwidth) = match in_elem.sty { + let (i_xn, in_elem_bitwidth) = match in_elem.kind { ty::Int(i) => ( args[0].immediate(), i.bit_width().unwrap_or(bx.data_layout().pointer_size.bits() as _) @@ -1288,7 +1315,7 @@ } } } - let ety = match in_elem.sty { + let ety = match in_elem.kind { ty::Float(f) if f.bit_width() == 32 => { if in_len < 2 || in_len > 16 { return_error!( @@ -1375,7 +1402,7 @@ // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81 fn llvm_vector_str(elem_ty: Ty<'_>, vec_len: usize, no_pointers: usize) -> String { let p0s: String = "p0".repeat(no_pointers); - match elem_ty.sty { + match elem_ty.kind { ty::Int(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()), ty::Uint(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()), ty::Float(v) => format!("v{}{}f{}", vec_len, p0s, v.bit_width()), @@ -1386,7 +1413,7 @@ fn llvm_vector_ty(cx: &CodegenCx<'ll, '_>, elem_ty: Ty<'_>, vec_len: usize, mut no_pointers: usize) -> &'ll Type { // FIXME: use cx.layout_of(ty).llvm_type() ? - let mut elem_ty = match elem_ty.sty { + let mut elem_ty = match elem_ty.kind { ty::Int(v) => cx.type_int_from_ty( v), ty::Uint(v) => cx.type_uint_from_ty( v), ty::Float(v) => cx.type_float_from_ty( v), @@ -1430,7 +1457,7 @@ // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { - match t.sty { + match t.kind { ty::RawPtr(p) => 1 + ptr_count(p.ty), _ => 0, } @@ -1438,7 +1465,7 @@ // Non-ptr type fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.sty { + match t.kind { ty::RawPtr(p) => non_ptr(p.ty), _ => t, } @@ -1446,7 +1473,7 @@ // The second argument must be a simd vector with an element type that's a pointer // to the element type of the first argument - let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).sty { + let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind { ty::RawPtr(p) if p.ty == in_elem => (ptr_count(arg_tys[1].simd_type(tcx)), non_ptr(arg_tys[1].simd_type(tcx))), _ => { @@ -1463,7 +1490,7 @@ assert_eq!(underlying_ty, non_ptr(arg_tys[0].simd_type(tcx))); // The element type of the third argument must be a signed integer type of any width: - match arg_tys[2].simd_type(tcx).sty { + match arg_tys[2].simd_type(tcx).kind { ty::Int(_) => (), _ => { require!(false, "expected element type `{}` of third argument `{}` \ @@ -1529,7 +1556,7 @@ // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { - match t.sty { + match t.kind { ty::RawPtr(p) => 1 + ptr_count(p.ty), _ => 0, } @@ -1537,7 +1564,7 @@ // Non-ptr type fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.sty { + match t.kind { ty::RawPtr(p) => non_ptr(p.ty), _ => t, } @@ -1545,7 +1572,7 @@ // The second argument must be a simd vector with an element type that's a pointer // to the element type of the first argument - let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).sty { + let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind { ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::MutMutable => (ptr_count(arg_tys[1].simd_type(tcx)), non_ptr(arg_tys[1].simd_type(tcx))), @@ -1563,7 +1590,7 @@ assert_eq!(underlying_ty, non_ptr(arg_tys[0].simd_type(tcx))); // The element type of the third argument must be a signed integer type of any width: - match arg_tys[2].simd_type(tcx).sty { + match arg_tys[2].simd_type(tcx).kind { ty::Int(_) => (), _ => { require!(false, "expected element type `{}` of third argument `{}` \ @@ -1612,7 +1639,7 @@ require!(ret_ty == in_elem, "expected return type `{}` (element of input `{}`), found `{}`", in_elem, in_ty, ret_ty); - return match in_elem.sty { + return match in_elem.kind { ty::Int(_) | ty::Uint(_) => { let r = bx.$integer_reduce(args[0].immediate()); if $ordered { @@ -1669,7 +1696,7 @@ require!(ret_ty == in_elem, "expected return type `{}` (element of input `{}`), found `{}`", in_elem, in_ty, ret_ty); - return match in_elem.sty { + return match in_elem.kind { ty::Int(_i) => { Ok(bx.$int_red(args[0].immediate(), true)) }, @@ -1704,7 +1731,7 @@ in_elem, in_ty, ret_ty); args[0].immediate() } else { - match in_elem.sty { + match in_elem.kind { ty::Int(_) | ty::Uint(_) => {}, _ => { return_error!("unsupported {} from `{}` with element `{}` to `{}`", @@ -1717,7 +1744,7 @@ let i1xn = bx.type_vector(i1, in_len as u64); bx.trunc(args[0].immediate(), i1xn) }; - return match in_elem.sty { + return match in_elem.kind { ty::Int(_) | ty::Uint(_) => { let r = bx.$red(input); Ok( @@ -1758,7 +1785,7 @@ enum Style { Float, Int(/* is signed? */ bool), Unsupported } - let (in_style, in_width) = match in_elem.sty { + let (in_style, in_width) = match in_elem.kind { // vectors of pointer-sized integers should've been // disallowed before here, so this unwrap is safe. ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), @@ -1766,7 +1793,7 @@ ty::Float(f) => (Style::Float, f.bit_width()), _ => (Style::Unsupported, 0) }; - let (out_style, out_width) = match out_elem.sty { + let (out_style, out_width) = match out_elem.kind { ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()), ty::Float(f) => (Style::Float, f.bit_width()), @@ -1816,7 +1843,7 @@ macro_rules! arith { ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { $(if name == stringify!($name) { - match in_elem.sty { + match in_elem.kind { $($(ty::$p(_))|* => { return Ok(bx.$call(args[0].immediate(), args[1].immediate())) })* @@ -1850,7 +1877,7 @@ let rhs = args[1].immediate(); let is_add = name == "simd_saturating_add"; let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _; - let (signed, elem_width, elem_ty) = match in_elem.sty { + let (signed, elem_width, elem_ty) = match in_elem.kind { ty::Int(i) => ( true, @@ -1896,7 +1923,7 @@ // FIXME: there’s multiple of this functions, investigate using some of the already existing // stuffs. fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, bool)> { - match ty.sty { + match ty.kind { ty::Int(t) => Some((match t { ast::IntTy::Isize => cx.tcx.sess.target.isize_ty.bit_width().unwrap() as u64, ast::IntTy::I8 => 8, @@ -1920,7 +1947,7 @@ // Returns the width of a float Ty // Returns None if the type is not a float fn float_type_width(ty: Ty<'_>) -> Option<u64> { - match ty.sty { + match ty.kind { ty::Float(t) => Some(t.bit_width() as u64), _ => None, }
diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs index 34e39af..e7562c3 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs
@@ -19,7 +19,6 @@ #![feature(link_args)] #![feature(static_nobundle)] #![feature(trusted_len)] -#![feature(mem_take)] use back::write::{create_target_machine, create_informational_target_machine}; use syntax_pos::symbol::Symbol; @@ -31,6 +30,7 @@ #[macro_use] extern crate rustc; extern crate rustc_target; #[macro_use] extern crate rustc_data_structures; +extern crate rustc_index; extern crate rustc_incremental; extern crate rustc_codegen_utils; extern crate rustc_codegen_ssa; @@ -38,7 +38,8 @@ extern crate rustc_driver as _; #[macro_use] extern crate log; -#[macro_use] extern crate syntax; +extern crate smallvec; +extern crate syntax; extern crate syntax_pos; extern crate rustc_errors as errors; @@ -48,15 +49,14 @@ use rustc_codegen_ssa::CompiledModule; use errors::{FatalError, Handler}; use rustc::dep_graph::WorkProduct; -use syntax::ext::allocator::AllocatorKind; -use syntax_pos::symbol::InternedString; +use syntax::expand::allocator::AllocatorKind; pub use llvm_util::target_features; use std::any::Any; -use std::sync::{mpsc, Arc}; +use std::sync::Arc; use std::ffi::CStr; use rustc::dep_graph::DepGraph; -use rustc::middle::cstore::{EncodedMetadata, MetadataLoader}; +use rustc::middle::cstore::{EncodedMetadata, MetadataLoaderDyn}; use rustc::session::Session; use rustc::session::config::{OutputFilenames, OutputType, PrintRequest, OptLevel}; use rustc::ty::{self, TyCtxt}; @@ -64,8 +64,6 @@ use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_utils::codegen_backend::CodegenBackend; -mod error_codes; - mod back { pub mod archive; pub mod bytecode; @@ -122,8 +120,12 @@ ) { unsafe { allocator::codegen(tcx, mods, kind) } } - fn compile_codegen_unit(&self, tcx: TyCtxt<'_>, cgu_name: InternedString) { - base::compile_codegen_unit(tcx, cgu_name); + fn compile_codegen_unit( + &self, tcx: TyCtxt<'_>, + cgu_name: Symbol, + tx: &std::sync::mpsc::Sender<Box<dyn Any + Send>>, + ) { + base::compile_codegen_unit(tcx, cgu_name, tx); } fn target_machine_factory( &self, @@ -254,28 +256,19 @@ llvm_util::print_version(); } - fn diagnostics(&self) -> &[(&'static str, &'static str)] { - &error_codes::DIAGNOSTICS - } - fn target_features(&self, sess: &Session) -> Vec<Symbol> { target_features(sess) } - fn metadata_loader(&self) -> Box<dyn MetadataLoader + Sync> { + fn metadata_loader(&self) -> Box<MetadataLoaderDyn> { box metadata::LlvmMetadataLoader } fn provide(&self, providers: &mut ty::query::Providers<'_>) { - rustc_codegen_utils::symbol_names::provide(providers); - rustc_codegen_ssa::back::symbol_export::provide(providers); - rustc_codegen_ssa::base::provide_both(providers); attributes::provide(providers); } fn provide_extern(&self, providers: &mut ty::query::Providers<'_>) { - rustc_codegen_ssa::back::symbol_export::provide_extern(providers); - rustc_codegen_ssa::base::provide_both(providers); attributes::provide_extern(providers); } @@ -284,10 +277,9 @@ tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool, - rx: mpsc::Receiver<Box<dyn Any + Send>>, ) -> Box<dyn Any> { box rustc_codegen_ssa::base::codegen_crate( - LlvmCodegenBackend(()), tcx, metadata, need_metadata_module, rx) + LlvmCodegenBackend(()), tcx, metadata, need_metadata_module) } fn join_codegen_and_link( @@ -320,8 +312,9 @@ // Run the linker on any artifacts that resulted from the LLVM run. // This should produce either a finished executable or library. - sess.profiler(|p| p.start_activity("link_crate")); time(sess, "linking", || { + let _prof_timer = sess.prof.generic_activity("link_crate"); + use rustc_codegen_ssa::back::link::link_binary; use crate::back::archive::LlvmArchiveBuilder; @@ -334,7 +327,6 @@ target_cpu, ); }); - sess.profiler(|p| p.end_activity("link_crate")); // Now that we won't touch anything in the incremental compilation directory // any more, we can finalize it (which involves renaming it)
diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index b07214f..c69942e 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs
@@ -50,7 +50,7 @@ } /// LLVMRustLinkage -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq)] #[repr(C)] pub enum Linkage { ExternalLinkage = 0, @@ -67,7 +67,6 @@ } // LLVMRustVisibility -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[repr(C)] pub enum Visibility { Default = 0, @@ -510,6 +509,7 @@ extern { pub type Context; } extern { pub type Type; } extern { pub type Value; } +extern { pub type ConstantInt; } extern { pub type Metadata; } extern { pub type BasicBlock; } #[repr(C)] @@ -719,8 +719,8 @@ pub fn LLVMConstInt(IntTy: &Type, N: c_ulonglong, SignExtend: Bool) -> &Value; pub fn LLVMConstIntOfArbitraryPrecision(IntTy: &Type, Wn: c_uint, Ws: *const u64) -> &Value; pub fn LLVMConstReal(RealTy: &Type, N: f64) -> &Value; - pub fn LLVMConstIntGetZExtValue(ConstantVal: &Value) -> c_ulonglong; - pub fn LLVMRustConstInt128Get(ConstantVal: &Value, SExt: bool, + pub fn LLVMConstIntGetZExtValue(ConstantVal: &ConstantInt) -> c_ulonglong; + pub fn LLVMRustConstInt128Get(ConstantVal: &ConstantInt, SExt: bool, high: &mut u64, low: &mut u64) -> bool; @@ -1666,7 +1666,7 @@ #[allow(improper_ctypes)] pub fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString); - pub fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&Value>; + pub fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; pub fn LLVMRustPassKind(Pass: &Pass) -> PassKind; pub fn LLVMRustFindAndCreatePass(Pass: *const c_char) -> Option<&'static mut Pass>;
diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs index 541d3d9..85e0b6d 100644 --- a/src/librustc_codegen_llvm/llvm_util.rs +++ b/src/librustc_codegen_llvm/llvm_util.rs
@@ -3,7 +3,7 @@ use syntax_pos::symbol::Symbol; use rustc::session::Session; use rustc::session::config::PrintRequest; -use rustc_target::spec::MergeFunctions; +use rustc_target::spec::{MergeFunctions, PanicStrategy}; use libc::c_int; use std::ffi::CString; use syntax::feature_gate::UnstableFeatures; @@ -73,6 +73,11 @@ } } + if sess.target.target.target_os == "emscripten" && + sess.panic_strategy() == PanicStrategy::Unwind { + add("-enable-emscripten-cxx-exceptions"); + } + // HACK(eddyb) LLVM inserts `llvm.assume` calls to preserve align attributes // during inlining. Unfortunately these may block other optimizations. add("-preserve-alignment-assumptions-during-inlining=false"); @@ -257,8 +262,7 @@ "hexagon" => HEXAGON_WHITELIST, "mips" | "mips64" => MIPS_WHITELIST, "powerpc" | "powerpc64" => POWERPC_WHITELIST, - // wasm32 on emscripten does not support these target features - "wasm32" if !sess.target.target.options.is_like_emscripten => WASM_WHITELIST, + "wasm32" => WASM_WHITELIST, _ => &[], } }
diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs index 36a9ff0..d921bbc 100644 --- a/src/librustc_codegen_llvm/type_of.rs +++ b/src/librustc_codegen_llvm/type_of.rs
@@ -43,7 +43,7 @@ layout::Abi::Aggregate { .. } => {} } - let name = match layout.ty.sty { + let name = match layout.ty.kind { ty::Closure(..) | ty::Generator(..) | ty::Adt(..) | @@ -56,16 +56,16 @@ let printer = DefPathBasedNames::new(cx.tcx, true, true); printer.push_type_name(layout.ty, &mut name, false); if let (&ty::Adt(def, _), &layout::Variants::Single { index }) - = (&layout.ty.sty, &layout.variants) + = (&layout.ty.kind, &layout.variants) { if def.is_enum() && !def.variants.is_empty() { write!(&mut name, "::{}", def.variants[index].ident).unwrap(); } } if let (&ty::Generator(_, substs, _), &layout::Variants::Single { index }) - = (&layout.ty.sty, &layout.variants) + = (&layout.ty.kind, &layout.variants) { - write!(&mut name, "::{}", substs.variant_name(index)).unwrap(); + write!(&mut name, "::{}", substs.as_generator().variant_name(index)).unwrap(); } Some(name) } @@ -226,7 +226,7 @@ if let Some(&llty) = cx.scalar_lltypes.borrow().get(&self.ty) { return llty; } - let llty = match self.ty.sty { + let llty = match self.ty.kind { ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { cx.type_ptr_to(cx.layout_of(ty).llvm_type(cx)) @@ -318,7 +318,7 @@ index: usize, immediate: bool) -> &'a Type { // HACK(eddyb) special-case fat pointers until LLVM removes // pointee types, to avoid bitcasting every `OperandRef::deref`. - match self.ty.sty { + match self.ty.kind { ty::Ref(..) | ty::RawPtr(_) => { return self.field(cx, index).llvm_type(cx);
diff --git a/src/librustc_codegen_ssa/Cargo.toml b/src/librustc_codegen_ssa/Cargo.toml index bc028d6..2eaae50 100644 --- a/src/librustc_codegen_ssa/Cargo.toml +++ b/src/librustc_codegen_ssa/Cargo.toml
@@ -10,14 +10,13 @@ test = false [dependencies] -bitflags = "1.0.4" +bitflags = "1.2.1" cc = "1.0.1" num_cpus = "1.0" memmap = "0.6" log = "0.4.5" libc = "0.2.44" jobserver = "0.1.11" -parking_lot = "0.9" tempfile = "3.1" rustc_serialize = { path = "../libserialize", package = "serialize" } @@ -30,4 +29,5 @@ rustc_errors = { path = "../librustc_errors" } rustc_fs_util = { path = "../librustc_fs_util" } rustc_incremental = { path = "../librustc_incremental" } +rustc_index = { path = "../librustc_index" } rustc_target = { path = "../librustc_target" }
diff --git a/src/librustc_codegen_ssa/README.md b/src/librustc_codegen_ssa/README.md index c8bb2e7..2a3a4fc 100644 --- a/src/librustc_codegen_ssa/README.md +++ b/src/librustc_codegen_ssa/README.md
@@ -1,121 +1,3 @@ -# Refactoring of `rustc_codegen_llvm` -by Denis Merigoux, October 23rd 2018 +Please read the rustc-guide chapter on [Backend Agnostic Codegen][bac]. -## State of the code before the refactoring - -All the code related to the compilation of MIR into LLVM IR was contained inside the `rustc_codegen_llvm` crate. Here is the breakdown of the most important elements: -* the `back` folder (7,800 LOC) implements the mechanisms for creating the different object files and archive through LLVM, but also the communication mechanisms for parallel code generation; -* the `debuginfo` (3,200 LOC) folder contains all code that passes debug information down to LLVM; -* the `llvm` (2,200 LOC) folder defines the FFI necessary to communicate with LLVM using the C++ API; -* the `mir` (4,300 LOC) folder implements the actual lowering from MIR to LLVM IR; -* the `base.rs` (1,300 LOC) file contains some helper functions but also the high-level code that launches the code generation and distributes the work. -* the `builder.rs` (1,200 LOC) file contains all the functions generating individual LLVM IR instructions inside a basic block; -* the `common.rs` (450 LOC) contains various helper functions and all the functions generating LLVM static values; -* the `type_.rs` (300 LOC) defines most of the type translations to LLVM IR. - -The goal of this refactoring is to separate inside this crate code that is specific to the LLVM from code that can be reused for other rustc backends. For instance, the `mir` folder is almost entirely backend-specific but it relies heavily on other parts of the crate. The separation of the code must not affect the logic of the code nor its performance. - -For these reasons, the separation process involves two transformations that have to be done at the same time for the resulting code to compile : - -1. replace all the LLVM-specific types by generics inside function signatures and structure definitions; -2. encapsulate all functions calling the LLVM FFI inside a set of traits that will define the interface between backend-agnostic code and the backend. - -While the LLVM-specific code will be left in `rustc_codegen_llvm`, all the new traits and backend-agnostic code will be moved in `rustc_codegen_ssa` (name suggestion by @eddyb). - -## Generic types and structures - -@irinagpopa started to parametrize the types of `rustc_codegen_llvm` by a generic `Value` type, implemented in LLVM by a reference `&'ll Value`. This work has been extended to all structures inside the `mir` folder and elsewhere, as well as for LLVM's `BasicBlock` and `Type` types. - -The two most important structures for the LLVM codegen are `CodegenCx` and `Builder`. They are parametrized by multiple lifetime parameters and the type for `Value`. - -```rust -struct CodegenCx<'ll, 'tcx> { - /* ... */ -} - -struct Builder<'a, 'll, 'tcx> { - cx: &'a CodegenCx<'ll, 'tcx>, - /* ... */ -} -``` - -`CodegenCx` is used to compile one codegen-unit that can contain multiple functions, whereas `Builder` is created to compile one basic block. - -The code in `rustc_codegen_llvm` has to deal with multiple explicit lifetime parameters, that correspond to the following: -* `'tcx` is the longest lifetime, that corresponds to the original `TyCtxt` containing the program's information; -* `'a` is a short-lived reference of a `CodegenCx` or another object inside a struct; -* `'ll` is the lifetime of references to LLVM objects such as `Value` or `Type`. - -Although there are already many lifetime parameters in the code, making it generic uncovered situations where the borrow-checker was passing only due to the special nature of the LLVM objects manipulated (they are extern pointers). For instance, a additional lifetime parameter had to be added to `LocalAnalyser` in `analyse.rs`, leading to the definition: - -```rust -struct LocalAnalyzer<'mir, 'a, 'tcx> { - /* ... */ -} -``` - -However, the two most important structures `CodegenCx` and `Builder` are not defined in the backend-agnostic code. Indeed, their content is highly specific of the backend and it makes more sense to leave their definition to the backend implementor than to allow just a narrow spot via a generic field for the backend's context. - -## Traits and interface - -Because they have to be defined by the backend, `CodegenCx` and `Builder` will be the structures implementing all the traits defining the backend's interface. These traits are defined in the folder `rustc_codegen_ssa/traits` and all the backend-agnostic code is parametrized by them. For instance, let us explain how a function in `base.rs` is parametrized: - -```rust -pub fn codegen_instance<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - cx: &'a Bx::CodegenCx, - instance: Instance<'tcx> -) { - /* ... */ -} -``` - -In this signature, we have the two lifetime parameters explained earlier and the master type `Bx` which satisfies the trait `BuilderMethods` corresponding to the interface satisfied by the `Builder` struct. The `BuilderMethods` defines an associated type `Bx::CodegenCx` that itself satisfies the `CodegenMethods` traits implemented by the struct `CodegenCx`. - -On the trait side, here is an example with part of the definition of `BuilderMethods` in `traits/builder.rs`: - -```rust -pub trait BuilderMethods<'a, 'tcx>: - HasCodegen<'tcx> - + DebugInfoBuilderMethods<'tcx> - + ArgTypeMethods<'tcx> - + AbiBuilderMethods<'tcx> - + IntrinsicCallMethods<'tcx> - + AsmBuilderMethods<'tcx> -{ - fn new_block<'b>( - cx: &'a Self::CodegenCx, - llfn: Self::Value, - name: &'b str - ) -> Self; - /* ... */ - fn cond_br( - &mut self, - cond: Self::Value, - then_llbb: Self::BasicBlock, - else_llbb: Self::BasicBlock, - ); - /* ... */ -} -``` - -Finally, a master structure implementing the `ExtraBackendMethods` trait is used for high-level codegen-driving functions like `codegen_crate` in `base.rs`. For LLVM, it is the empty `LlvmCodegenBackend`. `ExtraBackendMethods` should be implemented by the same structure that implements the `CodegenBackend` defined in `rustc_codegen_utils/codegen_backend.rs`. - -During the traitification process, certain functions have been converted from methods of a local structure to methods of `CodegenCx` or `Builder` and a corresponding `self` parameter has been added. Indeed, LLVM stores information internally that it can access when called through its API. This information does not show up in a Rust data structure carried around when these methods are called. However, when implementing a Rust backend for `rustc`, these methods will need information from `CodegenCx`, hence the additional parameter (unused in the LLVM implementation of the trait). - -## State of the code after the refactoring - -The traits offer an API which is very similar to the API of LLVM. This is not the best solution since LLVM has a very special way of doing things: when addding another backend, the traits definition might be changed in order to offer more flexibility. - -However, the current separation between backend-agnostic and LLVM-specific code has allows the reuse of a significant part of the old `rustc_codegen_llvm`. Here is the new LOC breakdown between backend-agnostic (BA) and LLVM for the most important elements: - -* `back` folder: 3,800 (BA) vs 4,100 (LLVM); -* `mir` folder: 4,400 (BA) vs 0 (LLVM); -* `base.rs`: 1,100 (BA) vs 250 (LLVM); -* `builder.rs`: 1,400 (BA) vs 0 (LLVM); -* `common.rs`: 350 (BA) vs 350 (LLVM); - -The `debuginfo` folder has been left almost untouched by the splitting and is specific to LLVM. Only its high-level features have been traitified. - -The new `traits` folder has 1500 LOC only for trait definitions. Overall, the 27,000 LOC-sized old `rustc_codegen_llvm` code has been split into the new 18,500 LOC-sized new `rustc_codegen_llvm` and the 12,000 LOC-sized `rustc_codegen_ssa`. We can say that this refactoring allowed the reuse of approximately 10,000 LOC that would otherwise have had to be duplicated between the multiple backends of `rustc`. - -The refactored version of `rustc`'s backend introduced no regression over the test suite nor in performance benchmark, which is in coherence with the nature of the refactoring that used only compile-time parametricity (no trait objects). +[bac]: https://rust-lang.github.io/rustc-guide/codegen/backend-agnostic.html
diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 9b044d9..a2b50ea 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs
@@ -3,7 +3,7 @@ use rustc::session::{Session, filesearch}; use rustc::session::config::{ - self, RUST_CGU_EXT, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer + self, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer }; use rustc::session::search_paths::PathKind; use rustc::middle::dependency_format::Linkage; @@ -15,7 +15,8 @@ use rustc_target::spec::{PanicStrategy, RelroLevel, LinkerFlavor}; use syntax::symbol::Symbol; -use crate::{METADATA_FILENAME, RLIB_BYTECODE_EXTENSION, CrateInfo, CodegenResults}; +use crate::{METADATA_FILENAME, RLIB_BYTECODE_EXTENSION, CrateInfo, + looks_like_rust_object_file, CodegenResults}; use super::archive::ArchiveBuilder; use super::command::Command; use super::linker::Linker; @@ -219,15 +220,24 @@ (linker.to_path_buf(), cmd) } -pub fn each_linked_rlib(sess: &Session, - info: &CrateInfo, - f: &mut dyn FnMut(CrateNum, &Path)) -> Result<(), String> { +pub fn each_linked_rlib( + info: &CrateInfo, + f: &mut dyn FnMut(CrateNum, &Path), +) -> Result<(), String> { let crates = info.used_crates_static.iter(); - let fmts = sess.dependency_formats.borrow(); - let fmts = fmts.get(&config::CrateType::Executable) - .or_else(|| fmts.get(&config::CrateType::Staticlib)) - .or_else(|| fmts.get(&config::CrateType::Cdylib)) - .or_else(|| fmts.get(&config::CrateType::ProcMacro)); + let mut fmts = None; + for (ty, list) in info.dependency_formats.iter() { + match ty { + config::CrateType::Executable | + config::CrateType::Staticlib | + config::CrateType::Cdylib | + config::CrateType::ProcMacro => { + fmts = Some(list); + break; + } + _ => {} + } + } let fmts = match fmts { Some(f) => f, None => return Err("could not find formats for rlibs".to_string()) @@ -314,6 +324,7 @@ NativeLibraryKind::NativeStatic => {} NativeLibraryKind::NativeStaticNobundle | NativeLibraryKind::NativeFramework | + NativeLibraryKind::NativeRawDylib | NativeLibraryKind::NativeUnknown => continue, } if let Some(name) = lib.name { @@ -406,7 +417,7 @@ tempdir); let mut all_native_libs = vec![]; - let res = each_linked_rlib(sess, &codegen_results.crate_info, &mut |cnum, path| { + let res = each_linked_rlib(&codegen_results.crate_info, &mut |cnum, path| { let name = &codegen_results.crate_info.crate_name[&cnum]; let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; @@ -874,7 +885,8 @@ Some(format!("-framework {}", name)) }, // These are included, no need to print them - NativeLibraryKind::NativeStatic => None, + NativeLibraryKind::NativeStatic | + NativeLibraryKind::NativeRawDylib => None, } }) .collect(); @@ -1284,7 +1296,11 @@ NativeLibraryKind::NativeUnknown => cmd.link_dylib(name), NativeLibraryKind::NativeFramework => cmd.link_framework(name), NativeLibraryKind::NativeStaticNobundle => cmd.link_staticlib(name), - NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(name, &search_path) + NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(name, &search_path), + NativeLibraryKind::NativeRawDylib => { + // FIXME(#58713): Proper handling for raw dylibs. + bug!("raw_dylib feature not yet implemented"); + }, } } } @@ -1294,11 +1310,13 @@ // Rust crates are not considered at all when creating an rlib output. All // dependencies will be linked when producing the final output (instead of // the intermediate rlib version) -fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(cmd: &mut dyn Linker, - sess: &'a Session, - codegen_results: &CodegenResults, - crate_type: config::CrateType, - tmpdir: &Path) { +fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( + cmd: &mut dyn Linker, + sess: &'a Session, + codegen_results: &CodegenResults, + crate_type: config::CrateType, + tmpdir: &Path, +) { // All of the heavy lifting has previously been accomplished by the // dependency_format module of the compiler. This is just crawling the // output of that module, adding crates as necessary. @@ -1307,8 +1325,10 @@ // will slurp up the object files inside), and linking to a dynamic library // involves just passing the right -l flag. - let formats = sess.dependency_formats.borrow(); - let data = formats.get(&crate_type).unwrap(); + let (_, data) = codegen_results.crate_info.dependency_formats + .iter() + .find(|(ty, _)| *ty == crate_type) + .expect("failed to find crate type in dependency format list"); // Invoke get_used_crates to ensure that we get a topological sorting of // crates. @@ -1372,7 +1392,9 @@ _ if codegen_results.crate_info.profiler_runtime == Some(cnum) => { add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum); } - _ if codegen_results.crate_info.sanitizer_runtime == Some(cnum) => { + _ if codegen_results.crate_info.sanitizer_runtime == Some(cnum) && + crate_type == config::CrateType::Executable => { + // Link the sanitizer runtimes only if we are actually producing an executable link_sanitizer_runtime::<B>(cmd, sess, codegen_results, tmpdir, cnum); } // compiler-builtins are always placed last to ensure that they're @@ -1514,7 +1536,7 @@ let name = cratepath.file_name().unwrap().to_str().unwrap(); let name = &name[3..name.len() - 5]; // chop off lib/.rlib - time_ext(sess.time_extended(), Some(sess), &format!("altering {}.rlib", name), || { + time_ext(sess.time_extended(), &format!("altering {}.rlib", name), || { let mut archive = <B as ArchiveBuilder>::new(sess, &dst, Some(cratepath)); archive.update_symbols(); @@ -1528,23 +1550,9 @@ let canonical = f.replace("-", "_"); let canonical_name = name.replace("-", "_"); - // Look for `.rcgu.o` at the end of the filename to conclude - // that this is a Rust-related object file. - fn looks_like_rust(s: &str) -> bool { - let path = Path::new(s); - let ext = path.extension().and_then(|s| s.to_str()); - if ext != Some(OutputType::Object.extension()) { - return false - } - let ext2 = path.file_stem() - .and_then(|s| Path::new(s).extension()) - .and_then(|s| s.to_str()); - ext2 == Some(RUST_CGU_EXT) - } - let is_rust_object = canonical.starts_with(&canonical_name) && - looks_like_rust(&f); + looks_like_rust_object_file(&f); // If we've been requested to skip all native object files // (those not generated by the rust compiler) then we can skip @@ -1620,10 +1628,12 @@ // generic function calls a native function, then the generic function must // be instantiated in the target crate, meaning that the native symbol must // also be resolved in the target crate. -pub fn add_upstream_native_libraries(cmd: &mut dyn Linker, - sess: &Session, - codegen_results: &CodegenResults, - crate_type: config::CrateType) { +pub fn add_upstream_native_libraries( + cmd: &mut dyn Linker, + sess: &Session, + codegen_results: &CodegenResults, + crate_type: config::CrateType, +) { // Be sure to use a topological sorting of crates because there may be // interdependencies between native libraries. When passing -nodefaultlibs, // for example, almost all native libraries depend on libc, so we have to @@ -1633,8 +1643,10 @@ // This passes RequireStatic, but the actual requirement doesn't matter, // we're just getting an ordering of crate numbers, we're not worried about // the paths. - let formats = sess.dependency_formats.borrow(); - let data = formats.get(&crate_type).unwrap(); + let (_, data) = codegen_results.crate_info.dependency_formats + .iter() + .find(|(ty, _)| *ty == crate_type) + .expect("failed to find crate type in dependency format list"); let crates = &codegen_results.crate_info.used_crates_static; for &(cnum, _) in crates { @@ -1661,7 +1673,11 @@ // ignore statically included native libraries here as we've // already included them when we included the rust library // previously - NativeLibraryKind::NativeStatic => {} + NativeLibraryKind::NativeStatic => {}, + NativeLibraryKind::NativeRawDylib => { + // FIXME(#58713): Proper handling for raw dylibs. + bug!("raw_dylib feature not yet implemented"); + }, } } }
diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs index c42cd02..999cc40 100644 --- a/src/librustc_codegen_ssa/back/linker.rs +++ b/src/librustc_codegen_ssa/back/linker.rs
@@ -1092,18 +1092,31 @@ } } - let formats = tcx.sess.dependency_formats.borrow(); - let deps = formats[&crate_type].iter(); + let formats = tcx.dependency_formats(LOCAL_CRATE); + let deps = formats.iter().filter_map(|(t, list)| { + if *t == crate_type { + Some(list) + } else { + None + } + }).next().unwrap(); - for (index, dep_format) in deps.enumerate() { + for (index, dep_format) in deps.iter().enumerate() { let cnum = CrateNum::new(index + 1); // For each dependency that we are linking to statically ... if *dep_format == Linkage::Static { // ... we add its symbol list to our export list. for &(symbol, level) in tcx.exported_symbols(cnum).iter() { - if level.is_below_threshold(export_threshold) { - symbols.push(symbol.symbol_name(tcx).to_string()); + if !level.is_below_threshold(export_threshold) { + continue; } + + // FIXME rust-lang/rust#64319, rust-lang/rust#64872: + // We want to block export of generics from dylibs, + // but we must fix rust-lang/rust#65890 before we can + // do that robustly. + + symbols.push(symbol.symbol_name(tcx).to_string()); } } }
diff --git a/src/librustc_codegen_ssa/back/symbol_export.rs b/src/librustc_codegen_ssa/back/symbol_export.rs index 7e700e6..85a9045 100644 --- a/src/librustc_codegen_ssa/back/symbol_export.rs +++ b/src/librustc_codegen_ssa/back/symbol_export.rs
@@ -13,8 +13,8 @@ use rustc::ty::query::Providers; use rustc::ty::subst::SubstsRef; use rustc::util::nodemap::{FxHashMap, DefIdMap}; -use rustc_data_structures::indexed_vec::IndexVec; -use syntax::ext::allocator::ALLOCATOR_METHODS; +use rustc_index::vec::IndexVec; +use syntax::expand::allocator::ALLOCATOR_METHODS; pub type ExportedSymbols = FxHashMap< CrateNum, @@ -94,14 +94,14 @@ // Only consider nodes that actually have exported symbols. Node::Item(&hir::Item { - node: hir::ItemKind::Static(..), + kind: hir::ItemKind::Static(..), .. }) | Node::Item(&hir::Item { - node: hir::ItemKind::Fn(..), .. + kind: hir::ItemKind::Fn(..), .. }) | Node::ImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Method(..), + kind: hir::ImplItemKind::Method(..), .. }) => { let def_id = tcx.hir().local_def_id(hir_id); @@ -298,7 +298,7 @@ }; for &cnum in cnums.iter() { - for &(ref exported_symbol, _) in tcx.exported_symbols(cnum).iter() { + for (exported_symbol, _) in tcx.exported_symbols(cnum).iter() { if let &ExportedSymbol::Generic(def_id, substs) = exported_symbol { let substs_map = instances.entry(def_id).or_default(); @@ -364,10 +364,11 @@ codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); if is_extern && !std_internal { - // Emscripten cannot export statics, so reduce their export level here - if tcx.sess.target.target.options.is_like_emscripten { + let target = &tcx.sess.target.target.llvm_target; + // WebAssembly cannot export data symbols, so reduce their export level + if target.contains("wasm32") || target.contains("emscripten") { if let Some(Node::Item(&hir::Item { - node: hir::ItemKind::Static(..), + kind: hir::ItemKind::Static(..), .. })) = tcx.hir().get_if_local(sym_def_id) { return SymbolExportLevel::Rust;
diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index 1bba479..b302b9a 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs
@@ -19,19 +19,19 @@ use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc::ty::TyCtxt; use rustc::util::common::{time_depth, set_time_depth, print_time_passes_entry}; -use rustc::util::profiling::SelfProfiler; +use rustc::util::profiling::SelfProfilerRef; use rustc_fs_util::link_or_copy; use rustc_data_structures::svh::Svh; -use rustc_errors::{Handler, Level, FatalError, DiagnosticId}; +use rustc_data_structures::sync::Lrc; +use rustc_errors::{Handler, Level, FatalError, DiagnosticId, SourceMapperDyn}; use rustc_errors::emitter::{Emitter}; use rustc_target::spec::MergeFunctions; use syntax::attr; -use syntax::ext::hygiene::ExpnId; +use syntax_pos::hygiene::ExpnId; use syntax_pos::symbol::{Symbol, sym}; use jobserver::{Client, Acquired}; use std::any::Any; -use std::borrow::Cow; use std::fs; use std::io; use std::mem; @@ -143,15 +143,12 @@ // Copy what clang does by turning on loop vectorization at O2 and // slp vectorization at O3. Otherwise configure other optimization aspects // of this pass manager builder. - // Turn off vectorization for emscripten, as it's not very well supported. self.vectorize_loop = !sess.opts.cg.no_vectorize_loops && (sess.opts.optimize == config::OptLevel::Default || - sess.opts.optimize == config::OptLevel::Aggressive) && - !sess.target.target.options.is_like_emscripten; + sess.opts.optimize == config::OptLevel::Aggressive); self.vectorize_slp = !sess.opts.cg.no_vectorize_slp && - sess.opts.optimize == config::OptLevel::Aggressive && - !sess.target.target.options.is_like_emscripten; + sess.opts.optimize == config::OptLevel::Aggressive; // Some targets (namely, NVPTX) interact badly with the MergeFunctions // pass. This is because MergeFunctions can generate new function calls @@ -196,42 +193,13 @@ } } -pub struct ProfileGenericActivityTimer { - profiler: Option<Arc<SelfProfiler>>, - label: Cow<'static, str>, -} - -impl ProfileGenericActivityTimer { - pub fn start( - profiler: Option<Arc<SelfProfiler>>, - label: Cow<'static, str>, - ) -> ProfileGenericActivityTimer { - if let Some(profiler) = &profiler { - profiler.start_activity(label.clone()); - } - - ProfileGenericActivityTimer { - profiler, - label, - } - } -} - -impl Drop for ProfileGenericActivityTimer { - fn drop(&mut self) { - if let Some(profiler) = &self.profiler { - profiler.end_activity(self.label.clone()); - } - } -} - /// Additional resources used by optimize_and_codegen (not module specific) #[derive(Clone)] pub struct CodegenContext<B: WriteBackendMethods> { // Resources needed when running LTO pub backend: B, pub time_passes: bool, - pub profiler: Option<Arc<SelfProfiler>>, + pub prof: SelfProfilerRef, pub lto: Lto, pub no_landing_pads: bool, pub save_temps: bool, @@ -283,31 +251,6 @@ ModuleKind::Allocator => &self.allocator_module_config, } } - - #[inline(never)] - #[cold] - fn profiler_active<F: FnOnce(&SelfProfiler) -> ()>(&self, f: F) { - match &self.profiler { - None => bug!("profiler_active() called but there was no profiler active"), - Some(profiler) => { - f(&*profiler); - } - } - } - - #[inline(always)] - pub fn profile<F: FnOnce(&SelfProfiler) -> ()>(&self, f: F) { - if unlikely!(self.profiler.is_some()) { - self.profiler_active(f) - } - } - - pub fn profile_activity( - &self, - label: impl Into<Cow<'static, str>>, - ) -> ProfileGenericActivityTimer { - ProfileGenericActivityTimer::start(self.profiler.clone(), label.into()) - } } fn generate_lto_work<B: ExtraBackendMethods>( @@ -316,7 +259,7 @@ needs_thin_lto: Vec<(String, B::ThinBuffer)>, import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)> ) -> Vec<(WorkItem<B>, u64)> { - cgcx.profile(|p| p.start_activity("codegen_run_lto")); + let _prof_timer = cgcx.prof.generic_activity("codegen_generate_lto_work"); let (lto_modules, copy_jobs) = if !needs_fat_lto.is_empty() { assert!(needs_thin_lto.is_empty()); @@ -343,8 +286,6 @@ }), 0) })).collect(); - cgcx.profile(|p| p.end_activity("codegen_run_lto")); - result } @@ -376,10 +317,11 @@ backend: B, tcx: TyCtxt<'_>, metadata: EncodedMetadata, - coordinator_receive: Receiver<Box<dyn Any + Send>>, total_cgus: usize, ) -> OngoingCodegen<B> { + let (coordinator_send, coordinator_receive) = channel(); let sess = tcx.sess; + let crate_name = tcx.crate_name(LOCAL_CRATE); let crate_hash = tcx.crate_hash(LOCAL_CRATE); let no_builtins = attr::contains_name(&tcx.hir().krate().attrs, sym::no_builtins); @@ -500,7 +442,8 @@ sess.jobserver.clone(), Arc::new(modules_config), Arc::new(metadata_config), - Arc::new(allocator_config)); + Arc::new(allocator_config), + coordinator_send.clone()); OngoingCodegen { backend, @@ -511,7 +454,7 @@ linker_info, crate_info, - coordinator_send: tcx.tx_to_llvm_workers.lock().clone(), + coordinator_send, codegen_worker_receive, shared_emitter_main, future: coordinator_thread, @@ -731,11 +674,11 @@ } } - pub fn name(&self) -> String { + fn profiling_event_id(&self) -> &'static str { match *self { - WorkItem::Optimize(ref m) => format!("optimize: {}", m.name), - WorkItem::CopyPostLtoArtifacts(ref m) => format!("copy post LTO artifacts: {}", m.name), - WorkItem::LTO(ref m) => format!("lto: {}", m.name()), + WorkItem::Optimize(_) => "codegen_module_optimize", + WorkItem::CopyPostLtoArtifacts(_) => "codegen_copy_artifacts_from_incr_cache", + WorkItem::LTO(_) => "codegen_module_perform_lto", } } } @@ -1005,8 +948,9 @@ modules_config: Arc<ModuleConfig>, metadata_config: Arc<ModuleConfig>, allocator_config: Arc<ModuleConfig>, + tx_to_llvm_workers: Sender<Box<dyn Any + Send>>, ) -> thread::JoinHandle<Result<CompiledModules, ()>> { - let coordinator_send = tcx.tx_to_llvm_workers.lock().clone(); + let coordinator_send = tx_to_llvm_workers; let sess = tcx.sess; // Compute the set of symbols we need to retain when doing LTO (if we need to) @@ -1048,7 +992,7 @@ }).expect("failed to spawn helper thread"); let mut each_linked_rlib_for_lto = Vec::new(); - drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| { + drop(link::each_linked_rlib(crate_info, &mut |cnum, path| { if link::ignored_for_lto(sess, crate_info, cnum) { return } @@ -1086,7 +1030,7 @@ save_temps: sess.opts.cg.save_temps, opts: Arc::new(sess.opts.clone()), time_passes: sess.time_extended(), - profiler: sess.self_profiling.clone(), + prof: sess.prof.clone(), exported_symbols, plugin_passes: sess.plugin_llvm_passes.borrow().clone(), remark: sess.opts.cg.remark.clone(), @@ -1643,12 +1587,8 @@ // as a diagnostic was already sent off to the main thread - just // surface that there was an error in this worker. bomb.result = { - let label = work.name(); - cgcx.profile(|p| p.start_activity(label.clone())); - let result = execute_work_item(&cgcx, work).ok(); - cgcx.profile(|p| p.end_activity(label)); - - result + let _prof_timer = cgcx.prof.generic_activity(work.profiling_event_id()); + execute_work_item(&cgcx, work).ok() }; }); } @@ -1724,13 +1664,13 @@ } impl Emitter for SharedEmitter { - fn emit_diagnostic(&mut self, db: &rustc_errors::Diagnostic) { + fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) { drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - msg: db.message(), - code: db.code.clone(), - lvl: db.level, + msg: diag.message(), + code: diag.code.clone(), + lvl: diag.level, }))); - for child in &db.children { + for child in &diag.children { drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { msg: child.message(), code: None, @@ -1739,6 +1679,9 @@ } drop(self.sender.send(SharedEmitterMessage::AbortIfErrors)); } + fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> { + None + } } impl SharedEmitterMain { @@ -1857,7 +1800,7 @@ // These are generally cheap and won't throw off scheduling. let cost = 0; - submit_codegened_module_to_llvm(&self.backend, tcx, module, cost); + submit_codegened_module_to_llvm(&self.backend, &self.coordinator_send, module, cost); } pub fn codegen_finished(&self, tcx: TyCtxt<'_>) { @@ -1899,12 +1842,12 @@ pub fn submit_codegened_module_to_llvm<B: ExtraBackendMethods>( _backend: &B, - tcx: TyCtxt<'_>, + tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>, module: ModuleCodegen<B::Module>, cost: u64, ) { let llvm_work_item = WorkItem::Optimize(module); - drop(tcx.tx_to_llvm_workers.lock().send(Box::new(Message::CodegenDone::<B> { + drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone::<B> { llvm_work_item, cost, }))); @@ -1912,11 +1855,11 @@ pub fn submit_post_lto_module_to_llvm<B: ExtraBackendMethods>( _backend: &B, - tcx: TyCtxt<'_>, + tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>, module: CachedModuleCodegen, ) { let llvm_work_item = WorkItem::CopyPostLtoArtifacts(module); - drop(tcx.tx_to_llvm_workers.lock().send(Box::new(Message::CodegenDone::<B> { + drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone::<B> { llvm_work_item, cost: 0, }))); @@ -1925,6 +1868,7 @@ pub fn submit_pre_lto_module_to_llvm<B: ExtraBackendMethods>( _backend: &B, tcx: TyCtxt<'_>, + tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>, module: CachedModuleCodegen, ) { let filename = pre_lto_bitcode_filename(&module.name); @@ -1939,7 +1883,7 @@ }) }; // Schedule the module to be loaded - drop(tcx.tx_to_llvm_workers.lock().send(Box::new(Message::AddImportOnlyModule::<B> { + drop(tx_to_llvm_workers.send(Box::new(Message::AddImportOnlyModule::<B> { module_data: SerializedModule::FromUncompressedFile(mmap), work_product: module.source, })));
diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index 4acbe03..ee4ec7f 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs
@@ -29,25 +29,22 @@ use rustc::session::config::{self, EntryFnType, Lto}; use rustc::session::Session; use rustc::util::nodemap::FxHashMap; -use rustc_data_structures::indexed_vec::Idx; +use rustc_index::vec::Idx; use rustc_codegen_utils::{symbol_names_test, check_for_rustc_errors_attr}; use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; use crate::mir::place::PlaceRef; use crate::back::write::{OngoingCodegen, start_async_codegen, submit_pre_lto_module_to_llvm, submit_post_lto_module_to_llvm}; use crate::{MemFlags, CrateInfo}; -use crate::callee; use crate::common::{RealPredicate, TypeKind, IntPredicate}; use crate::meth; use crate::mir; use crate::traits::*; -use std::any::Any; use std::cmp; use std::ops::{Deref, DerefMut}; use std::time::{Instant, Duration}; -use std::sync::mpsc; use syntax_pos::Span; use syntax::attr; use rustc::hir; @@ -96,7 +93,7 @@ ret_ty: Bx::Type, op: hir::BinOpKind, ) -> Bx::Value { - let signed = match t.sty { + let signed = match t.kind { ty::Float(_) => { let cmp = bin_op_to_fcmp_predicate(op); let cmp = bx.fcmp(cmp, lhs, rhs); @@ -130,7 +127,7 @@ ) -> Cx::Value { let (source, target) = cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, cx.param_env()); - match (&source.sty, &target.sty) { + match (&source.kind, &target.kind) { (&ty::Array(_, len), &ty::Slice(_)) => { cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all())) } @@ -160,7 +157,7 @@ dst_ty: Ty<'tcx>, ) -> (Bx::Value, Bx::Value) { debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty); - match (&src_ty.sty, &dst_ty.sty) { + match (&src_ty.kind, &dst_ty.kind) { (&ty::Ref(_, a, _), &ty::Ref(_, b, _)) | (&ty::Ref(_, a, _), @@ -171,12 +168,6 @@ let ptr_ty = bx.cx().type_ptr_to(bx.cx().backend_type(bx.cx().layout_of(b))); (bx.pointercast(src, ptr_ty), unsized_info(bx.cx(), a, b, None)) } - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { - let (a, b) = (src_ty.boxed_ty(), dst_ty.boxed_ty()); - assert!(bx.cx().type_is_sized(a)); - let ptr_ty = bx.cx().type_ptr_to(bx.cx().backend_type(bx.cx().layout_of(b))); - (bx.pointercast(src, ptr_ty), unsized_info(bx.cx(), a, b, None)) - } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); @@ -199,6 +190,8 @@ } let (lldata, llextra) = result.unwrap(); // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + // FIXME(eddyb) move these out of this `match` arm, so they're always + // applied, uniformly, no matter the source/destination types. (bx.bitcast(lldata, bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true)), bx.bitcast(llextra, bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true))) } @@ -215,31 +208,27 @@ ) { let src_ty = src.layout.ty; let dst_ty = dst.layout.ty; - let mut coerce_ptr = || { - let (base, info) = match bx.load_operand(src).val { - OperandValue::Pair(base, info) => { - // fat-ptr to fat-ptr unsize preserves the vtable - // i.e., &'a fmt::Debug+Send => &'a fmt::Debug - // So we need to pointercast the base to ensure - // the types match up. - let thin_ptr = dst.layout.field(bx.cx(), FAT_PTR_ADDR); - (bx.pointercast(base, bx.cx().backend_type(thin_ptr)), info) - } - OperandValue::Immediate(base) => { - unsize_thin_ptr(bx, base, src_ty, dst_ty) - } - OperandValue::Ref(..) => bug!() - }; - OperandValue::Pair(base, info).store(bx, dst); - }; - match (&src_ty.sty, &dst_ty.sty) { + match (&src_ty.kind, &dst_ty.kind) { (&ty::Ref(..), &ty::Ref(..)) | (&ty::Ref(..), &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => { - coerce_ptr() - } - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { - coerce_ptr() + let (base, info) = match bx.load_operand(src).val { + OperandValue::Pair(base, info) => { + // fat-ptr to fat-ptr unsize preserves the vtable + // i.e., &'a fmt::Debug+Send => &'a fmt::Debug + // So we need to pointercast the base to ensure + // the types match up. + // FIXME(eddyb) use `scalar_pair_element_backend_type` here, + // like `unsize_thin_ptr` does. + let thin_ptr = dst.layout.field(bx.cx(), FAT_PTR_ADDR); + (bx.pointercast(base, bx.cx().backend_type(thin_ptr)), info) + } + OperandValue::Immediate(base) => { + unsize_thin_ptr(bx, base, src_ty, dst_ty) + } + OperandValue::Ref(..) => bug!() + }; + OperandValue::Pair(base, info).store(bx, dst); } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { @@ -379,8 +368,7 @@ let sig = instance.fn_sig(cx.tcx()); let sig = cx.tcx().normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - let lldecl = cx.instances().borrow().get(&instance).cloned().unwrap_or_else(|| - bug!("Instance `{:?}` not already declared", instance)); + let lldecl = cx.get_fn(instance); let mir = cx.tcx().instance_mir(instance.def); mir::codegen_mir::<Bx>(cx, lldecl, &mir, instance, sig); @@ -402,7 +390,7 @@ return; } - let main_llfn = cx.get_fn(instance); + let main_llfn = cx.get_fn_addr(instance); let et = cx.tcx().entry_fn(LOCAL_CRATE).map(|e| e.1); match et { @@ -418,8 +406,13 @@ rust_main_def_id: DefId, use_start_lang_item: bool, ) { - let llfty = - cx.type_func(&[cx.type_int(), cx.type_ptr_to(cx.type_i8p())], cx.type_int()); + // The entry function is either `int main(void)` or `int main(int argc, char **argv)`, + // depending on whether the target needs `argc` and `argv` to be passed in. + let llfty = if cx.sess().target.target.options.main_needs_argc_argv { + cx.type_func(&[cx.type_int(), cx.type_ptr_to(cx.type_i8p())], cx.type_int()) + } else { + cx.type_func(&[], cx.type_int()) + }; let main_ret_ty = cx.tcx().fn_sig(rust_main_def_id).output(); // Given that `main()` has no arguments, @@ -449,18 +442,17 @@ bx.insert_reference_to_gdb_debug_scripts_section_global(); - // Params from native main() used as args for rust start function - let param_argc = bx.get_param(0); - let param_argv = bx.get_param(1); - let arg_argc = bx.intcast(param_argc, cx.type_isize(), true); - let arg_argv = param_argv; + let (arg_argc, arg_argv) = get_argc_argv(cx, &mut bx); let (start_fn, args) = if use_start_lang_item { let start_def_id = cx.tcx().require_lang_item(StartFnLangItem, None); - let start_fn = callee::resolve_and_get_fn( - cx, - start_def_id, - cx.tcx().intern_substs(&[main_ret_ty.into()]), + let start_fn = cx.get_fn_addr( + ty::Instance::resolve( + cx.tcx(), + ty::ParamEnv::reveal_all(), + start_def_id, + cx.tcx().intern_substs(&[main_ret_ty.into()]), + ).unwrap() ); (start_fn, vec![bx.pointercast(rust_main, cx.type_ptr_to(cx.type_i8p())), arg_argc, arg_argv]) @@ -475,6 +467,27 @@ } } +/// Obtain the `argc` and `argv` values to pass to the rust start function. +fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, + bx: &mut Bx +) -> (Bx::Value, Bx::Value) +{ + if cx.sess().target.target.options.main_needs_argc_argv { + // Params from native `main()` used as args for rust start function + let param_argc = bx.get_param(0); + let param_argv = bx.get_param(1); + let arg_argc = bx.intcast(param_argc, cx.type_isize(), true); + let arg_argv = param_argv; + (arg_argc, arg_argv) + } else { + // The Rust start function doesn't need `argc` and `argv`, so just pass zeros. + let arg_argc = bx.const_int(cx.type_int(), 0); + let arg_argv = bx.const_null(cx.type_ptr_to(cx.type_i8p())); + (arg_argc, arg_argv) + } +} + pub const CODEGEN_WORKER_ID: usize = ::std::usize::MAX; pub fn codegen_crate<B: ExtraBackendMethods>( @@ -482,19 +495,13 @@ tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool, - rx: mpsc::Receiver<Box<dyn Any + Send>>, ) -> OngoingCodegen<B> { check_for_rustc_errors_attr(tcx); // Skip crate items and just output metadata in -Z no-codegen mode. if tcx.sess.opts.debugging_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { - let ongoing_codegen = start_async_codegen( - backend, - tcx, - metadata, - rx, - 1); + let ongoing_codegen = start_async_codegen(backend, tcx, metadata, 1); ongoing_codegen.codegen_finished(tcx); @@ -519,16 +526,11 @@ // unnecessarily. if tcx.dep_graph.is_fully_enabled() { for cgu in &codegen_units { - tcx.codegen_unit(cgu.name().clone()); + tcx.codegen_unit(cgu.name()); } } - let ongoing_codegen = start_async_codegen( - backend.clone(), - tcx, - metadata, - rx, - codegen_units.len()); + let ongoing_codegen = start_async_codegen(backend.clone(), tcx, metadata, codegen_units.len()); let ongoing_codegen = AbortCodegenOnDrop::<B>(Some(ongoing_codegen)); // Codegen an allocator shim, if necessary. @@ -539,7 +541,7 @@ // linkage, then it's already got an allocator shim and we'll be using that // one instead. If nothing exists then it's our job to generate the // allocator! - let any_dynamic_crate = tcx.sess.dependency_formats.borrow() + let any_dynamic_crate = tcx.dependency_formats(LOCAL_CRATE) .iter() .any(|(_, list)| { use rustc::middle::dependency_format::Linkage; @@ -572,8 +574,6 @@ if need_metadata_module { // Codegen the encoded metadata. - tcx.sess.profiler(|p| p.start_activity("codegen crate metadata")); - let metadata_cgu_name = cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata")).as_str() @@ -583,7 +583,6 @@ backend.write_compressed_metadata(tcx, &ongoing_codegen.metadata, &mut metadata_llvm_module); }); - tcx.sess.profiler(|p| p.end_activity("codegen crate metadata")); let metadata_module = ModuleCodegen { name: metadata_cgu_name, @@ -612,22 +611,22 @@ match cgu_reuse { CguReuse::No => { - tcx.sess.profiler(|p| p.start_activity(format!("codegen {}", cgu.name()))); let start_time = Instant::now(); - backend.compile_codegen_unit(tcx, *cgu.name()); + backend.compile_codegen_unit(tcx, cgu.name(), &ongoing_codegen.coordinator_send); total_codegen_time += start_time.elapsed(); - tcx.sess.profiler(|p| p.end_activity(format!("codegen {}", cgu.name()))); false } CguReuse::PreLto => { - submit_pre_lto_module_to_llvm(&backend, tcx, CachedModuleCodegen { + submit_pre_lto_module_to_llvm(&backend, tcx, &ongoing_codegen.coordinator_send, + CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.work_product(tcx), }); true } CguReuse::PostLto => { - submit_post_lto_module_to_llvm(&backend, tcx, CachedModuleCodegen { + submit_post_lto_module_to_llvm(&backend, &ongoing_codegen.coordinator_send, + CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.work_product(tcx), }); @@ -731,6 +730,7 @@ used_crate_source: Default::default(), lang_item_to_crate: Default::default(), missing_lang_items: Default::default(), + dependency_formats: tcx.dependency_formats(LOCAL_CRATE), }; let lang_items = tcx.lang_items();
diff --git a/src/librustc_codegen_ssa/callee.rs b/src/librustc_codegen_ssa/callee.rs deleted file mode 100644 index 4744dd6..0000000 --- a/src/librustc_codegen_ssa/callee.rs +++ /dev/null
@@ -1,36 +0,0 @@ -use crate::traits::*; -use rustc::ty; -use rustc::ty::subst::SubstsRef; -use rustc::hir::def_id::DefId; - -pub fn resolve_and_get_fn<'tcx, Cx: CodegenMethods<'tcx>>( - cx: &Cx, - def_id: DefId, - substs: SubstsRef<'tcx>, -) -> Cx::Value { - cx.get_fn( - ty::Instance::resolve( - cx.tcx(), - ty::ParamEnv::reveal_all(), - def_id, - substs - ).unwrap() - ) -} - -pub fn resolve_and_get_fn_for_vtable<'tcx, - Cx: Backend<'tcx> + MiscMethods<'tcx> + TypeMethods<'tcx> ->( - cx: &Cx, - def_id: DefId, - substs: SubstsRef<'tcx>, -) -> Cx::Value { - cx.get_fn( - ty::Instance::resolve_for_vtable( - cx.tcx(), - ty::ParamEnv::reveal_all(), - def_id, - substs - ).unwrap() - ) -}
diff --git a/src/librustc_codegen_ssa/common.rs b/src/librustc_codegen_ssa/common.rs index 6376512..ac39ca9 100644 --- a/src/librustc_codegen_ssa/common.rs +++ b/src/librustc_codegen_ssa/common.rs
@@ -1,6 +1,7 @@ #![allow(non_camel_case_types, non_snake_case)] use rustc::ty::{Ty, TyCtxt}; +use rustc::session::Session; use syntax_pos::Span; use rustc::hir::def_id::DefId; @@ -109,14 +110,11 @@ // for now we content ourselves with providing a no-op HashStable // implementation for CGUs. mod temp_stable_hash_impls { - use rustc_data_structures::stable_hasher::{StableHasherResult, StableHasher, - HashStable}; + use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use crate::ModuleCodegen; impl<HCX, M> HashStable<HCX> for ModuleCodegen<M> { - fn hash_stable<W: StableHasherResult>(&self, - _: &mut HCX, - _: &mut StableHasher<W>) { + fn hash_stable(&self, _: &mut HCX, _: &mut StableHasher) { // do nothing } } @@ -203,3 +201,7 @@ _ => bug!("shift_mask_val: expected Integer or Vector, found {:?}", kind), } } + +pub fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) { + span_err!(a, b, E0511, "{}", c); +}
diff --git a/src/librustc_codegen_ssa/debuginfo/mod.rs b/src/librustc_codegen_ssa/debuginfo/mod.rs index c9b1c02..d1a0cf7 100644 --- a/src/librustc_codegen_ssa/debuginfo/mod.rs +++ b/src/librustc_codegen_ssa/debuginfo/mod.rs
@@ -1,82 +1,2 @@ -use syntax_pos::{BytePos, Span}; -use rustc::hir::def_id::CrateNum; - +// FIXME(eddyb) find a place for this (or a way to replace it). pub mod type_names; - -pub enum FunctionDebugContext<D> { - RegularContext(FunctionDebugContextData<D>), - DebugInfoDisabled, - FunctionWithoutDebugInfo, -} - -impl<D> FunctionDebugContext<D> { - pub fn get_ref(&self, span: Span) -> &FunctionDebugContextData<D> { - match *self { - FunctionDebugContext::RegularContext(ref data) => data, - FunctionDebugContext::DebugInfoDisabled => { - span_bug!( - span, - "debuginfo: Error trying to access FunctionDebugContext \ - although debug info is disabled!", - ); - } - FunctionDebugContext::FunctionWithoutDebugInfo => { - span_bug!( - span, - "debuginfo: Error trying to access FunctionDebugContext \ - for function that should be ignored by debug info!", - ); - } - } - } -} - -/// Enables emitting source locations for the given functions. -/// -/// Since we don't want source locations to be emitted for the function prelude, -/// they are disabled when beginning to codegen a new function. This functions -/// switches source location emitting on and must therefore be called before the -/// first real statement/expression of the function is codegened. -pub fn start_emitting_source_locations<D>(dbg_context: &mut FunctionDebugContext<D>) { - match *dbg_context { - FunctionDebugContext::RegularContext(ref mut data) => { - data.source_locations_enabled = true; - }, - _ => { /* safe to ignore */ } - } -} - -pub struct FunctionDebugContextData<D> { - pub fn_metadata: D, - pub source_locations_enabled: bool, - pub defining_crate: CrateNum, -} - -pub enum VariableAccess<'a, V> { - // The llptr given is an alloca containing the variable's value - DirectVariable { alloca: V }, - // The llptr given is an alloca containing the start of some pointer chain - // leading to the variable's content. - IndirectVariable { alloca: V, address_operations: &'a [i64] } -} - -pub enum VariableKind { - ArgumentVariable(usize /*index*/), - LocalVariable, -} - - -#[derive(Clone, Copy, Debug)] -pub struct MirDebugScope<D> { - pub scope_metadata: Option<D>, - // Start and end offsets of the file to which this DIScope belongs. - // These are used to quickly determine whether some span refers to the same file. - pub file_start_pos: BytePos, - pub file_end_pos: BytePos, -} - -impl<D> MirDebugScope<D> { - pub fn is_valid(&self) -> bool { - !self.scope_metadata.is_none() - } -}
diff --git a/src/librustc_codegen_ssa/debuginfo/type_names.rs b/src/librustc_codegen_ssa/debuginfo/type_names.rs index 9b5ad94..166a74f 100644 --- a/src/librustc_codegen_ssa/debuginfo/type_names.rs +++ b/src/librustc_codegen_ssa/debuginfo/type_names.rs
@@ -32,7 +32,7 @@ // .natvis visualizers (and perhaps other existing native debuggers?) let cpp_like_names = tcx.sess.target.target.options.is_like_msvc; - match t.sty { + match t.kind { ty::Bool => output.push_str("bool"), ty::Char => output.push_str("char"), ty::Str => output.push_str("str"), @@ -221,7 +221,7 @@ output.push_str(&tcx.crate_name(def_id.krate).as_str()); for path_element in tcx.def_path(def_id).data { output.push_str("::"); - output.push_str(&path_element.data.as_interned_str().as_str()); + output.push_str(&path_element.data.as_symbol().as_str()); } } else { output.push_str(&tcx.item_name(def_id).as_str());
diff --git a/src/librustc_codegen_ssa/error_codes.rs b/src/librustc_codegen_ssa/error_codes.rs index 8ff41c2..02e26d8 100644 --- a/src/librustc_codegen_ssa/error_codes.rs +++ b/src/librustc_codegen_ssa/error_codes.rs
@@ -1,5 +1,40 @@ syntax::register_diagnostics! { +E0511: r##" +Invalid monomorphization of an intrinsic function was used. Erroneous code +example: + +```ignore (error-emitted-at-codegen-which-cannot-be-handled-by-compile_fail) +#![feature(platform_intrinsics)] + +extern "platform-intrinsic" { + fn simd_add<T>(a: T, b: T) -> T; +} + +fn main() { + unsafe { simd_add(0, 1); } + // error: invalid monomorphization of `simd_add` intrinsic +} +``` + +The generic type has to be a SIMD type. Example: + +``` +#![feature(repr_simd)] +#![feature(platform_intrinsics)] + +#[repr(simd)] +#[derive(Copy, Clone)] +struct i32x2(i32, i32); + +extern "platform-intrinsic" { + fn simd_add<T>(a: T, b: T) -> T; +} + +unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok! +``` +"##, + E0668: r##" Malformed inline assembly rejected by LLVM.
diff --git a/src/librustc_codegen_ssa/glue.rs b/src/librustc_codegen_ssa/glue.rs index 7fd9f67..9818bb7 100644 --- a/src/librustc_codegen_ssa/glue.rs +++ b/src/librustc_codegen_ssa/glue.rs
@@ -20,7 +20,7 @@ let align = bx.const_usize(layout.align.abi.bytes()); return (size, align); } - match t.sty { + match t.kind { ty::Dynamic(..) => { // load size/align from vtable let vtable = info.unwrap(); @@ -64,7 +64,7 @@ let size = bx.add(sized_size, unsized_size); // Packed types ignore the alignment of their fields. - if let ty::Adt(def, _) = t.sty { + if let ty::Adt(def, _) = t.kind { if def.repr.packed() { unsized_align = sized_align; }
diff --git a/src/librustc_codegen_ssa/lib.rs b/src/librustc_codegen_ssa/lib.rs index 1708d72..dd75883 100644 --- a/src/librustc_codegen_ssa/lib.rs +++ b/src/librustc_codegen_ssa/lib.rs
@@ -10,7 +10,6 @@ #![feature(in_band_lifetimes)] #![feature(nll)] #![feature(trusted_len)] -#![feature(mem_take)] #![feature(associated_type_bounds)] #![recursion_limit="256"] @@ -21,18 +20,19 @@ #[macro_use] extern crate log; #[macro_use] extern crate rustc; -#[macro_use] extern crate rustc_data_structures; #[macro_use] extern crate syntax; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use rustc::dep_graph::WorkProduct; -use rustc::session::config::{OutputFilenames, OutputType}; +use rustc::session::config::{OutputFilenames, OutputType, RUST_CGU_EXT}; use rustc::middle::lang_items::LangItem; use rustc::hir::def_id::CrateNum; +use rustc::ty::query::Providers; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::svh::Svh; use rustc::middle::cstore::{LibSource, CrateSource, NativeLibrary}; +use rustc::middle::dependency_format::Dependencies; use syntax_pos::symbol::Symbol; mod error_codes; @@ -42,7 +42,6 @@ pub mod mir; pub mod debuginfo; pub mod base; -pub mod callee; pub mod glue; pub mod meth; pub mod mono_item; @@ -63,6 +62,7 @@ pub const METADATA_FILENAME: &str = "rust.metadata.bin"; pub const RLIB_BYTECODE_EXTENSION: &str = "bc.z"; + impl<M> ModuleCodegen<M> { pub fn into_compiled_module(self, emit_obj: bool, @@ -142,6 +142,7 @@ pub used_crates_dynamic: Vec<(CrateNum, LibSource)>, pub lang_item_to_crate: FxHashMap<LangItem, CrateNum>, pub missing_lang_items: FxHashMap<CrateNum, Vec<LangItem>>, + pub dependency_formats: Lrc<Dependencies>, } @@ -156,3 +157,32 @@ pub linker_info: back::linker::LinkerInfo, pub crate_info: CrateInfo, } + +pub fn provide(providers: &mut Providers<'_>) { + crate::back::symbol_export::provide(providers); + crate::base::provide_both(providers); +} + +pub fn provide_extern(providers: &mut Providers<'_>) { + crate::back::symbol_export::provide_extern(providers); + crate::base::provide_both(providers); +} + +/// Checks if the given filename ends with the `.rcgu.o` extension that `rustc` +/// uses for the object files it generates. +pub fn looks_like_rust_object_file(filename: &str) -> bool { + let path = Path::new(filename); + let ext = path.extension().and_then(|s| s.to_str()); + if ext != Some(OutputType::Object.extension()) { + // The file name does not end with ".o", so it can't be an object file. + return false + } + + // Strip the ".o" at the end + let ext2 = path.file_stem() + .and_then(|s| Path::new(s).extension()) + .and_then(|s| s.to_str()); + + // Check if the "inner" extension + ext2 == Some(RUST_CGU_EXT) +}
diff --git a/src/librustc_codegen_ssa/meth.rs b/src/librustc_codegen_ssa/meth.rs index 7fe9f5f..266d2e5 100644 --- a/src/librustc_codegen_ssa/meth.rs +++ b/src/librustc_codegen_ssa/meth.rs
@@ -1,6 +1,5 @@ use rustc_target::abi::call::FnType; -use crate::callee; use crate::traits::*; use rustc::ty::{self, Ty, Instance}; @@ -92,7 +91,14 @@ let methods = methods.cloned().map(|opt_mth| { opt_mth.map_or(nullptr, |(def_id, substs)| { - callee::resolve_and_get_fn_for_vtable(cx, def_id, substs) + cx.get_fn_addr( + ty::Instance::resolve_for_vtable( + cx.tcx(), + ty::ParamEnv::reveal_all(), + def_id, + substs, + ).unwrap() + ) }) }); @@ -102,7 +108,7 @@ // `get_vtable` in rust_mir/interpret/traits.rs // ///////////////////////////////////////////////////////////////////////////////////////////// let components: Vec<_> = [ - cx.get_fn(Instance::resolve_drop_in_place(cx.tcx(), ty)), + cx.get_fn_addr(Instance::resolve_drop_in_place(cx.tcx(), ty)), cx.const_usize(layout.size.bytes()), cx.const_usize(layout.align.abi.bytes()) ].iter().cloned().chain(methods).collect();
diff --git a/src/librustc_codegen_ssa/mir/analyze.rs b/src/librustc_codegen_ssa/mir/analyze.rs index d192f2f..2e5dc3d 100644 --- a/src/librustc_codegen_ssa/mir/analyze.rs +++ b/src/librustc_codegen_ssa/mir/analyze.rs
@@ -1,12 +1,13 @@ //! An analysis to determine which locals require allocas and //! which do not. -use rustc_data_structures::bit_set::BitSet; +use rustc_index::bit_set::BitSet; use rustc_data_structures::graph::dominators::Dominators; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_index::vec::{Idx, IndexVec}; use rustc::mir::{self, Location, TerminatorKind}; use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext}; use rustc::mir::traversal; +use rustc::session::config::DebugInfo; use rustc::ty; use rustc::ty::layout::{LayoutOf, HasTyCtxt}; use syntax_pos::DUMMY_SP; @@ -21,13 +22,20 @@ analyzer.visit_body(mir); - for (index, (ty, span)) in mir.local_decls.iter() - .map(|l| (l.ty, l.source_info.span)) - .enumerate() + for (local, decl) in mir.local_decls.iter_enumerated() { - let ty = fx.monomorphize(&ty); - debug!("local {} has type {:?}", index, ty); - let layout = fx.cx.spanned_layout_of(ty, span); + // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead + // of putting everything in allocas just so we can use llvm.dbg.declare. + if fx.cx.sess().opts.debuginfo == DebugInfo::Full { + if mir.local_kind(local) == mir::LocalKind::Arg || decl.name.is_some() { + analyzer.not_ssa(local); + continue; + } + } + + let ty = fx.monomorphize(&decl.ty); + debug!("local {:?} has type `{}`", local, ty); + let layout = fx.cx.spanned_layout_of(ty, decl.source_info.span); if fx.cx.is_backend_immediate(layout) { // These sorts of types are immediates that we can store // in an Value without an alloca. @@ -40,7 +48,7 @@ // (e.g., structs) into an alloca unconditionally, just so // that we don't have to deal with having two pathways // (gep vs extractvalue etc). - analyzer.not_ssa(mir::Local::new(index)); + analyzer.not_ssa(local); } } @@ -191,10 +199,7 @@ location: Location) { debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue); - if let mir::Place { - base: mir::PlaceBase::Local(index), - projection: box [], - } = *place { + if let Some(index) = place.as_local() { self.assign(index, location); let decl_span = self.fx.mir.local_decls[index].source_info.span; if !self.fx.rvalue_creates_operand(rvalue, decl_span) { @@ -218,7 +223,7 @@ mir::TerminatorKind::Call { func: mir::Operand::Constant(ref c), ref args, .. - } => match c.literal.ty.sty { + } => match c.literal.ty.kind { ty::FnDef(did, _) => Some((did, args)), _ => None, },
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 1bb0ea5..7985531 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs
@@ -1,9 +1,10 @@ +use rustc_index::vec::Idx; use rustc::middle::lang_items; use rustc::ty::{self, Ty, TypeFoldable, Instance}; use rustc::ty::layout::{self, LayoutOf, HasTyCtxt, FnTypeExt}; -use rustc::mir::{self, Place, PlaceBase, Static, StaticKind}; +use rustc::mir::{self, PlaceBase, Static, StaticKind}; use rustc::mir::interpret::PanicInfo; -use rustc_target::abi::call::{ArgType, FnType, PassMode, IgnoreMode}; +use rustc_target::abi::call::{ArgType, FnType, PassMode}; use rustc_target::spec::abi::Abi; use crate::base; use crate::MemFlags; @@ -14,8 +15,7 @@ use std::borrow::Cow; -use syntax::symbol::Symbol; -use syntax_pos::Pos; +use syntax::{source_map::Span, symbol::Symbol}; use super::{FunctionCx, LocalRef}; use super::place::PlaceRef; @@ -148,6 +148,26 @@ } } } + + // Generate sideeffect intrinsic if jumping to any of the targets can form + // a loop. + fn maybe_sideeffect<'b, 'tcx2: 'b, Bx: BuilderMethods<'b, 'tcx2>>( + &self, + mir: &'b mir::Body<'tcx>, + bx: &mut Bx, + targets: &[mir::BasicBlock], + ) { + if bx.tcx().sess.opts.debugging_opts.insert_sideeffect { + if targets.iter().any(|target| { + *target <= *self.bb + && target + .start_location() + .is_predecessor_of(self.bb.start_location(), mir) + }) { + bx.sideeffect(); + } + } + } } /// Codegen implementations for some terminator variants. @@ -196,6 +216,7 @@ let lltrue = helper.llblock(self, targets[0]); let llfalse = helper.llblock(self, targets[1]); if switch_ty == bx.tcx().types.bool { + helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice()); // Don't generate trivial icmps when switching on bool if let [0] = values[..] { bx.cond_br(discr.immediate(), llfalse, lltrue); @@ -209,9 +230,11 @@ ); let llval = bx.const_uint_big(switch_llty, values[0]); let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval); + helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice()); bx.cond_br(cmp, lltrue, llfalse); } } else { + helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice()); let (otherwise, targets) = targets.split_last().unwrap(); bx.switch( discr.immediate(), @@ -224,14 +247,15 @@ } fn codegen_return_terminator(&mut self, mut bx: Bx) { + // Call `va_end` if this is the definition of a C-variadic function. if self.fn_ty.c_variadic { - match self.va_list_ref { - Some(va_list) => { + // The `VaList` "spoofed" argument is just after all the real arguments. + let va_list_arg_idx = self.fn_ty.args.len(); + match self.locals[mir::Local::new(1 + va_list_arg_idx)] { + LocalRef::Place(va_list) => { bx.va_end(va_list.llval); } - None => { - bug!("C-variadic function must have a `va_list_ref`"); - } + _ => bug!("C-variadic function must have a `VaList` place"), } } if self.fn_ty.ret.layout.abi.is_uninhabited() { @@ -242,15 +266,11 @@ return; } let llval = match self.fn_ty.ret.mode { - PassMode::Ignore(IgnoreMode::Zst) | PassMode::Indirect(..) => { + PassMode::Ignore | PassMode::Indirect(..) => { bx.ret_void(); return; } - PassMode::Ignore(IgnoreMode::CVarArgs) => { - bug!("C-variadic arguments should never be the return type"); - } - PassMode::Direct(_) | PassMode::Pair(..) => { let op = self.codegen_consume(&mut bx, &mir::Place::return_place().as_ref()); @@ -310,6 +330,7 @@ if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { // we don't actually need to drop anything. + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.funclet_br(self, &mut bx, target); return } @@ -323,7 +344,7 @@ args1 = [place.llval]; &args1[..] }; - let (drop_fn, fn_ty) = match ty.sty { + let (drop_fn, fn_ty) = match ty.kind { ty::Dynamic(..) => { let sig = drop_fn.fn_sig(self.cx.tcx()); let sig = self.cx.tcx().normalize_erasing_late_bound_regions( @@ -336,10 +357,11 @@ (meth::DESTRUCTOR.get_fn(&mut bx, vtable, &fn_ty), fn_ty) } _ => { - (bx.get_fn(drop_fn), + (bx.get_fn_addr(drop_fn), FnType::of_instance(&bx, drop_fn)) } }; + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.do_call(self, &mut bx, fn_ty, drop_fn, args, Some((ReturnDest::Nothing, target)), unwind); @@ -375,6 +397,7 @@ // Don't codegen the panic block if success if known. if const_cond == Some(expected) { + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.funclet_br(self, &mut bx, target); return; } @@ -385,6 +408,7 @@ // Create the failure block and the conditional branch to it. let lltarget = helper.llblock(self, target); let panic_block = self.new_block("panic"); + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); if expected { bx.cond_br(cond, lltarget, panic_block.llbb()); } else { @@ -396,38 +420,19 @@ self.set_debug_loc(&mut bx, terminator.source_info); // Get the location information. - let loc = bx.sess().source_map().lookup_char_pos(span.lo()); - let filename = Symbol::intern(&loc.file.name.to_string()); - let line = bx.const_u32(loc.line as u32); - let col = bx.const_u32(loc.col.to_usize() as u32 + 1); + let location = self.get_caller_location(&mut bx, span).immediate(); // Put together the arguments to the panic entry point. let (lang_item, args) = match msg { PanicInfo::BoundsCheck { ref len, ref index } => { let len = self.codegen_operand(&mut bx, len).immediate(); let index = self.codegen_operand(&mut bx, index).immediate(); - - let file_line_col = bx.static_panic_msg( - None, - filename, - line, - col, - "panic_bounds_check_loc", - ); - (lang_items::PanicBoundsCheckFnLangItem, - vec![file_line_col, index, len]) + (lang_items::PanicBoundsCheckFnLangItem, vec![location, index, len]) } _ => { let msg_str = Symbol::intern(msg.description()); - let msg_file_line_col = bx.static_panic_msg( - Some(msg_str), - filename, - line, - col, - "panic_loc", - ); - (lang_items::PanicFnLangItem, - vec![msg_file_line_col]) + let msg = bx.const_str(msg_str); + (lang_items::PanicFnLangItem, vec![msg.0, msg.1, location]) } }; @@ -435,7 +440,7 @@ let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item); let instance = ty::Instance::mono(bx.tcx(), def_id); let fn_ty = FnType::of_instance(&bx, instance); - let llfn = bx.get_fn(instance); + let llfn = bx.get_fn_addr(instance); // Codegen the actual panic invoke/call. helper.do_call(self, &mut bx, fn_ty, llfn, &args, None, cleanup); @@ -455,7 +460,7 @@ // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.codegen_operand(&mut bx, func); - let (instance, mut llfn) = match callee.layout.ty.sty { + let (instance, mut llfn) = match callee.layout.ty.kind { ty::FnDef(def_id, substs) => { (Some(ty::Instance::resolve(bx.tcx(), ty::ParamEnv::reveal_all(), @@ -488,6 +493,7 @@ if let Some(destination_ref) = destination.as_ref() { let &(ref dest, target) = destination_ref; self.codegen_transmute(&mut bx, &args[0], dest); + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.funclet_br(self, &mut bx, target); } else { // If we are trying to transmute to an uninhabited type, @@ -502,10 +508,7 @@ return; } - // The "spoofed" `VaListImpl` added to a C-variadic functions signature - // should not be included in the `extra_args` calculation. - let extra_args_start_idx = sig.inputs().len() - if sig.c_variadic { 1 } else { 0 }; - let extra_args = &args[extra_args_start_idx..]; + let extra_args = &args[sig.inputs().len()..]; let extra_args = extra_args.iter().map(|op_arg| { let op_ty = op_arg.ty(self.mir, bx.tcx()); self.monomorphize(&op_ty) @@ -518,6 +521,7 @@ Some(ty::InstanceDef::DropGlue(_, None)) => { // Empty drop glue; a no-op. let &(_, target) = destination.as_ref().unwrap(); + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.funclet_br(self, &mut bx, target); return; } @@ -529,44 +533,35 @@ let ty = instance.unwrap().substs.type_at(0); let layout = bx.layout_of(ty); if layout.abi.is_uninhabited() { - let loc = bx.sess().source_map().lookup_char_pos(span.lo()); - let filename = Symbol::intern(&loc.file.name.to_string()); - let line = bx.const_u32(loc.line as u32); - let col = bx.const_u32(loc.col.to_usize() as u32 + 1); - - let str = format!( - "Attempted to instantiate uninhabited type {}", - ty - ); - let msg_str = Symbol::intern(&str); - let msg_file_line_col = bx.static_panic_msg( - Some(msg_str), - filename, - line, - col, - "panic_loc", - ); + let msg_str = format!("Attempted to instantiate uninhabited type {}", ty); + let msg = bx.const_str(Symbol::intern(&msg_str)); + let location = self.get_caller_location(&mut bx, span).immediate(); // Obtain the panic entry point. let def_id = common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem); let instance = ty::Instance::mono(bx.tcx(), def_id); let fn_ty = FnType::of_instance(&bx, instance); - let llfn = bx.get_fn(instance); + let llfn = bx.get_fn_addr(instance); + if let Some((_, target)) = destination.as_ref() { + helper.maybe_sideeffect(self.mir, &mut bx, &[*target]); + } // Codegen the actual panic invoke/call. helper.do_call( self, &mut bx, fn_ty, llfn, - &[msg_file_line_col], + &[msg.0, msg.1, location], destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)), cleanup, ); } else { // a NOP - helper.funclet_br(self, &mut bx, destination.as_ref().unwrap().1) + let target = destination.as_ref().unwrap().1; + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + helper.funclet_br(self, &mut bx, target); } return; } @@ -584,6 +579,21 @@ ReturnDest::Nothing }; + if intrinsic == Some("caller_location") { + if let Some((_, target)) = destination.as_ref() { + let location = self.get_caller_location(&mut bx, span); + + if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { + location.val.store(&mut bx, tmp); + } + self.store_return(&mut bx, ret_dest, &fn_ty.ret, location.immediate()); + + helper.maybe_sideeffect(self.mir, &mut bx, &[*target]); + helper.funclet_br(self, &mut bx, *target); + } + return; + } + if intrinsic.is_some() && intrinsic != Some("drop_in_place") { let dest = match ret_dest { _ if fn_ty.ret.is_indirect() => llargs[0], @@ -601,53 +611,43 @@ // checked by const-qualification, which also // promotes any complex rvalues to constants. if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") { - match *arg { + match arg { // The shuffle array argument is usually not an explicit constant, // but specified directly in the code. This means it gets promoted // and we can then extract the value by evaluating the promoted. - mir::Operand::Copy( - Place { - base: PlaceBase::Static(box Static { - kind: StaticKind::Promoted(promoted, _), + mir::Operand::Copy(place) | mir::Operand::Move(place) => { + if let mir::PlaceRef { + base: + &PlaceBase::Static(box Static { + kind: StaticKind::Promoted(promoted, _), + ty, + def_id: _, + }), + projection: &[], + } = place.as_ref() + { + let param_env = ty::ParamEnv::reveal_all(); + let cid = mir::interpret::GlobalId { + instance: self.instance, + promoted: Some(promoted), + }; + let c = bx.tcx().const_eval(param_env.and(cid)); + let (llval, ty) = self.simd_shuffle_indices( + &bx, + terminator.source_info.span, ty, - def_id: _, - }), - projection: box [], + c, + ); + return OperandRef { + val: Immediate(llval), + layout: bx.layout_of(ty), + }; + } else { + span_bug!(span, "shuffle indices must be constant"); } - ) | - mir::Operand::Move( - Place { - base: PlaceBase::Static(box Static { - kind: StaticKind::Promoted(promoted, _), - ty, - def_id: _, - }), - projection: box [], - } - ) => { - let param_env = ty::ParamEnv::reveal_all(); - let cid = mir::interpret::GlobalId { - instance: self.instance, - promoted: Some(promoted), - }; - let c = bx.tcx().const_eval(param_env.and(cid)); - let (llval, ty) = self.simd_shuffle_indices( - &bx, - terminator.source_info.span, - ty, - c, - ); - return OperandRef { - val: Immediate(llval), - layout: bx.layout_of(ty), - }; + } - } - mir::Operand::Copy(_) | - mir::Operand::Move(_) => { - span_bug!(span, "shuffle indices must be constant"); - } - mir::Operand::Constant(ref constant) => { + mir::Operand::Constant(constant) => { let c = self.eval_mir_constant(constant); let (llval, ty) = self.simd_shuffle_indices( &bx, @@ -675,6 +675,7 @@ } if let Some((_, target)) = *destination { + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.funclet_br(self, &mut bx, target); } else { bx.unreachable(); @@ -691,26 +692,7 @@ (&args[..], None) }; - // Useful determining if the current argument is the "spoofed" `VaListImpl` - let last_arg_idx = if sig.inputs().is_empty() { - None - } else { - Some(sig.inputs().len() - 1) - }; 'make_args: for (i, arg) in first_args.iter().enumerate() { - // If this is a C-variadic function the function signature contains - // an "spoofed" `VaListImpl`. This argument is ignored, but we need to - // populate it with a dummy operand so that the users real arguments - // are not overwritten. - let i = if sig.c_variadic && last_arg_idx.map(|x| i >= x).unwrap_or(false) { - if i + 1 < fn_ty.args.len() { - i + 1 - } else { - break 'make_args - } - } else { - i - }; let mut op = self.codegen_operand(&mut bx, arg); if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { @@ -782,10 +764,13 @@ let fn_ptr = match (llfn, instance) { (Some(llfn), _) => llfn, - (None, Some(instance)) => bx.get_fn(instance), + (None, Some(instance)) => bx.get_fn_addr(instance), _ => span_bug!(span, "no llfn for call"), }; + if let Some((_, target)) = destination.as_ref() { + helper.maybe_sideeffect(self.mir, &mut bx, &[*target]); + } helper.do_call(self, &mut bx, fn_ty, fn_ptr, &llargs, destination.as_ref().map(|&(_, target)| (ret_dest, target)), cleanup); @@ -835,6 +820,7 @@ } mir::TerminatorKind::Goto { target } => { + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.funclet_br(self, &mut bx, target); } @@ -1004,6 +990,20 @@ } } + fn get_caller_location( + &mut self, + bx: &mut Bx, + span: Span, + ) -> OperandRef<'tcx, Bx::Value> { + let caller = bx.tcx().sess.source_map().lookup_char_pos(span.lo()); + let const_loc = bx.tcx().const_caller_location(( + Symbol::intern(&caller.file.name.to_string()), + caller.line as u32, + caller.col_display as u32 + 1, + )); + OperandRef::from_const(bx, const_loc) + } + fn get_personality_slot( &mut self, bx: &mut Bx @@ -1102,10 +1102,7 @@ if fn_ret.is_ignore() { return ReturnDest::Nothing; } - let dest = if let mir::Place { - base: mir::PlaceBase::Local(index), - projection: box [], - } = *dest { + let dest = if let Some(index) = dest.as_local() { match self.locals[index] { LocalRef::Place(dest) => dest, LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), @@ -1163,10 +1160,7 @@ src: &mir::Operand<'tcx>, dst: &mir::Place<'tcx> ) { - if let mir::Place { - base: mir::PlaceBase::Local(index), - projection: box [], - } = *dst { + if let Some(index) = dst.as_local() { match self.locals[index] { LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place), LocalRef::UnsizedPlace(_) => bug!("transmute must not involve unsized locals"),
diff --git a/src/librustc_codegen_ssa/mir/constant.rs b/src/librustc_codegen_ssa/mir/constant.rs index 216e5a4..72d098e 100644 --- a/src/librustc_codegen_ssa/mir/constant.rs +++ b/src/librustc_codegen_ssa/mir/constant.rs
@@ -1,6 +1,6 @@ use rustc::mir::interpret::ErrorHandled; use rustc::mir; -use rustc_data_structures::indexed_vec::Idx; +use rustc_index::vec::Idx; use rustc::ty::{self, Ty}; use rustc::ty::layout::{self, HasTyCtxt}; use syntax::source_map::Span; @@ -23,7 +23,10 @@ instance, promoted: None, }; - self.cx.tcx().const_eval(ty::ParamEnv::reveal_all().and(cid)) + self.cx.tcx().const_eval(ty::ParamEnv::reveal_all().and(cid)).map_err(|err| { + self.cx.tcx().sess.span_err(constant.span, "erroneous constant encountered"); + err + }) }, _ => Ok(self.monomorphize(&constant.literal)), } @@ -40,7 +43,7 @@ constant .map(|c| { let field_ty = c.ty.builtin_index().unwrap(); - let fields = match c.ty.sty { + let fields = match c.ty.kind { ty::Array(_, n) => n.eval_usize(bx.tcx(), ty::ParamEnv::reveal_all()), _ => bug!("invalid simd shuffle type: {}", c.ty), };
diff --git a/src/librustc_codegen_ssa/mir/debuginfo.rs b/src/librustc_codegen_ssa/mir/debuginfo.rs new file mode 100644 index 0000000..c215db3 --- /dev/null +++ b/src/librustc_codegen_ssa/mir/debuginfo.rs
@@ -0,0 +1,385 @@ +use rustc_index::vec::{Idx, IndexVec}; +use rustc::hir::def_id::CrateNum; +use rustc::mir; +use rustc::session::config::DebugInfo; +use rustc::ty::{self, TyCtxt}; +use rustc::ty::layout::{LayoutOf, Size, VariantIdx}; +use crate::traits::*; + +use syntax_pos::{BytePos, Span, Symbol}; +use syntax::symbol::kw; + +use super::{FunctionCx, LocalRef}; +use super::OperandValue; + +pub struct FunctionDebugContext<D> { + pub scopes: IndexVec<mir::SourceScope, DebugScope<D>>, + pub source_locations_enabled: bool, + pub defining_crate: CrateNum, +} + +#[derive(Copy, Clone)] +pub enum VariableKind { + ArgumentVariable(usize /*index*/), + LocalVariable, +} + +#[derive(Clone, Copy, Debug)] +pub struct DebugScope<D> { + pub scope_metadata: Option<D>, + // Start and end offsets of the file to which this DIScope belongs. + // These are used to quickly determine whether some span refers to the same file. + pub file_start_pos: BytePos, + pub file_end_pos: BytePos, +} + +impl<D> DebugScope<D> { + pub fn is_valid(&self) -> bool { + !self.scope_metadata.is_none() + } +} + +impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + pub fn set_debug_loc( + &mut self, + bx: &mut Bx, + source_info: mir::SourceInfo + ) { + let (scope, span) = self.debug_loc(source_info); + if let Some(debug_context) = &mut self.debug_context { + // FIXME(eddyb) get rid of this unwrap somehow. + bx.set_source_location(debug_context, scope.unwrap(), span); + } + } + + pub fn debug_loc(&self, source_info: mir::SourceInfo) -> (Option<Bx::DIScope>, Span) { + // Bail out if debug info emission is not enabled. + match self.debug_context { + None => return (None, source_info.span), + Some(_) => {} + } + + // In order to have a good line stepping behavior in debugger, we overwrite debug + // locations of macro expansions with that of the outermost expansion site + // (unless the crate is being compiled with `-Z debug-macros`). + if !source_info.span.from_expansion() || + self.cx.sess().opts.debugging_opts.debug_macros { + let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo()); + (scope, source_info.span) + } else { + // Walk up the macro expansion chain until we reach a non-expanded span. + // We also stop at the function body level because no line stepping can occur + // at the level above that. + let span = syntax_pos::hygiene::walk_chain(source_info.span, self.mir.span.ctxt()); + let scope = self.scope_metadata_for_loc(source_info.scope, span.lo()); + // Use span of the outermost expansion site, while keeping the original lexical scope. + (scope, span) + } + } + + // DILocations inherit source file name from the parent DIScope. Due to macro expansions + // it may so happen that the current span belongs to a different file than the DIScope + // corresponding to span's containing source scope. If so, we need to create a DIScope + // "extension" into that file. + fn scope_metadata_for_loc(&self, scope_id: mir::SourceScope, pos: BytePos) + -> Option<Bx::DIScope> { + let debug_context = self.debug_context.as_ref()?; + let scope_metadata = debug_context.scopes[scope_id].scope_metadata; + if pos < debug_context.scopes[scope_id].file_start_pos || + pos >= debug_context.scopes[scope_id].file_end_pos { + let sm = self.cx.sess().source_map(); + let defining_crate = debug_context.defining_crate; + Some(self.cx.extend_scope_to_file( + scope_metadata.unwrap(), + &sm.lookup_char_pos(pos).file, + defining_crate + )) + } else { + scope_metadata + } + } + + /// Apply debuginfo and/or name, after creating the `alloca` for a local, + /// or initializing the local with an operand (whichever applies). + // FIXME(eddyb) use `llvm.dbg.value` (which would work for operands), + // not just `llvm.dbg.declare` (which requires `alloca`). + pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) { + // FIXME(eddyb) maybe name the return place as `_0` or `return`? + if local == mir::RETURN_PLACE { + return; + } + + let vars = match &self.per_local_var_debug_info { + Some(per_local) => &per_local[local], + None => return, + }; + let whole_local_var = vars.iter().find(|var| { + var.place.projection.is_empty() + }); + let has_proj = || vars.iter().any(|var| { + !var.place.projection.is_empty() + }); + + let (fallback_var, kind) = if self.mir.local_kind(local) == mir::LocalKind::Arg { + let arg_index = local.index() - 1; + + // Add debuginfo even to unnamed arguments. + // FIXME(eddyb) is this really needed? + let var = if arg_index == 0 && has_proj() { + // Hide closure environments from debuginfo. + // FIXME(eddyb) shouldn't `ArgumentVariable` indices + // be offset to account for the hidden environment? + None + } else { + Some(VarDebugInfo { + name: kw::Invalid, + source_info: self.mir.local_decls[local].source_info, + place: local.into(), + }) + }; + (var, VariableKind::ArgumentVariable(arg_index + 1)) + } else { + (None, VariableKind::LocalVariable) + }; + + let local_ref = &self.locals[local]; + + if !bx.sess().fewer_names() { + let name = match whole_local_var.or(fallback_var.as_ref()) { + Some(var) if var.name != kw::Invalid => var.name.to_string(), + _ => format!("{:?}", local), + }; + match local_ref { + LocalRef::Place(place) | + LocalRef::UnsizedPlace(place) => { + bx.set_var_name(place.llval, &name); + } + LocalRef::Operand(Some(operand)) => match operand.val { + OperandValue::Ref(x, ..) | + OperandValue::Immediate(x) => { + bx.set_var_name(x, &name); + } + OperandValue::Pair(a, b) => { + // FIXME(eddyb) these are scalar components, + // maybe extract the high-level fields? + bx.set_var_name(a, &(name.clone() + ".0")); + bx.set_var_name(b, &(name + ".1")); + } + } + LocalRef::Operand(None) => {} + } + } + + if bx.sess().opts.debuginfo != DebugInfo::Full { + return; + } + + let debug_context = match &self.debug_context { + Some(debug_context) => debug_context, + None => return, + }; + + // FIXME(eddyb) add debuginfo for unsized places too. + let base = match local_ref { + LocalRef::Place(place) => place, + _ => return, + }; + + let vars = vars.iter().chain(if whole_local_var.is_none() { + fallback_var.as_ref() + } else { + None + }); + + for var in vars { + let mut layout = base.layout; + let mut direct_offset = Size::ZERO; + // FIXME(eddyb) use smallvec here. + let mut indirect_offsets = vec![]; + + let kind = if var.place.projection.is_empty() { + kind + } else { + VariableKind::LocalVariable + }; + + for elem in &var.place.projection[..] { + match *elem { + mir::ProjectionElem::Deref => { + indirect_offsets.push(Size::ZERO); + layout = bx.cx().layout_of( + layout.ty.builtin_deref(true) + .unwrap_or_else(|| { + span_bug!( + var.source_info.span, + "cannot deref `{}`", + layout.ty, + ) + }).ty, + ); + } + mir::ProjectionElem::Field(field, _) => { + let i = field.index(); + let offset = indirect_offsets.last_mut() + .unwrap_or(&mut direct_offset); + *offset += layout.fields.offset(i); + layout = layout.field(bx.cx(), i); + } + mir::ProjectionElem::Downcast(_, variant) => { + layout = layout.for_variant(bx.cx(), variant); + } + _ => span_bug!( + var.source_info.span, + "unsupported var debuginfo place `{:?}`", + var.place, + ), + } + } + + let (scope, span) = self.debug_loc(var.source_info); + if let Some(scope) = scope { + bx.declare_local(debug_context, var.name, layout.ty, scope, + base.llval, direct_offset, &indirect_offsets, kind, span); + } + } + } + + pub fn debug_introduce_locals(&self, bx: &mut Bx) { + if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() { + for local in self.locals.indices() { + self.debug_introduce_local(bx, local); + } + } + } +} + +pub fn per_local_var_debug_info( + tcx: TyCtxt<'tcx>, + body: &mir::Body<'tcx>, +) -> Option<IndexVec<mir::Local, Vec<VarDebugInfo<'tcx>>>> { + if tcx.sess.opts.debuginfo == DebugInfo::Full || !tcx.sess.fewer_names() { + let mut per_local = IndexVec::from_elem(vec![], &body.local_decls); + for (local, decl) in body.local_decls.iter_enumerated() { + if let Some(name) = decl.name { + per_local[local].push(VarDebugInfo { + name, + source_info: mir::SourceInfo { + span: decl.source_info.span, + scope: decl.visibility_scope, + }, + place: local.into(), + }); + } + } + + let upvar_debuginfo = &body.__upvar_debuginfo_codegen_only_do_not_use; + if !upvar_debuginfo.is_empty() { + + let env_arg = mir::Local::new(1); + let mut env_projs = vec![]; + + let pin_did = tcx.lang_items().pin_type(); + match body.local_decls[env_arg].ty.kind { + ty::RawPtr(_) | + ty::Ref(..) => { + env_projs.push(mir::ProjectionElem::Deref); + } + ty::Adt(def, substs) if Some(def.did) == pin_did => { + if let ty::Ref(..) = substs.type_at(0).kind { + env_projs.push(mir::ProjectionElem::Field( + mir::Field::new(0), + // HACK(eddyb) field types aren't used or needed here. + tcx.types.err, + )); + env_projs.push(mir::ProjectionElem::Deref); + } + } + _ => {} + } + + let extra_locals = { + let upvars = upvar_debuginfo + .iter() + .enumerate() + .map(|(i, upvar)| { + let source_info = mir::SourceInfo { + span: body.span, + scope: mir::OUTERMOST_SOURCE_SCOPE, + }; + (None, i, upvar.debug_name, upvar.by_ref, source_info) + }); + + let generator_fields = body.generator_layout.as_ref().map(|generator_layout| { + generator_layout.variant_fields.iter() + .enumerate() + .flat_map(move |(variant_idx, fields)| { + let variant_idx = Some(VariantIdx::from(variant_idx)); + fields.iter() + .enumerate() + .filter_map(move |(i, field)| { + let decl = &generator_layout. + __local_debuginfo_codegen_only_do_not_use[*field]; + if let Some(name) = decl.name { + let source_info = mir::SourceInfo { + span: decl.source_info.span, + scope: decl.visibility_scope, + }; + Some((variant_idx, i, name, false, source_info)) + } else { + None + } + }) + }) + }).into_iter().flatten(); + + upvars.chain(generator_fields) + }; + + for (variant_idx, field, name, by_ref, source_info) in extra_locals { + let mut projs = env_projs.clone(); + + if let Some(variant_idx) = variant_idx { + projs.push(mir::ProjectionElem::Downcast(None, variant_idx)); + } + + projs.push(mir::ProjectionElem::Field( + mir::Field::new(field), + // HACK(eddyb) field types aren't used or needed here. + tcx.types.err, + )); + + if by_ref { + projs.push(mir::ProjectionElem::Deref); + } + + per_local[env_arg].push(VarDebugInfo { + name, + source_info, + place: mir::Place { + base: mir::PlaceBase::Local(env_arg), + projection: tcx.intern_place_elems(&projs), + }, + }); + } + } + + Some(per_local) + } else { + None + } +} + +/// Debug information relatating to an user variable. +// FIXME(eddyb) move this to the MIR bodies themselves. +#[derive(Clone)] +pub struct VarDebugInfo<'tcx> { + pub name: Symbol, + + /// Source info of the user variable, including the scope + /// within which the variable is visible (to debuginfo) + /// (see `LocalDecl`'s `source_info` field for more details). + pub source_info: mir::SourceInfo, + + /// Where the data for this user variable is to be found. + pub place: mir::Place<'tcx>, +}
diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index aa3971a..5ad1445 100644 --- a/src/librustc_codegen_ssa/mir/mod.rs +++ b/src/librustc_codegen_ssa/mir/mod.rs
@@ -1,22 +1,17 @@ -use rustc::ty::{self, Ty, TypeFoldable, UpvarSubsts, Instance}; +use rustc::ty::{self, Ty, TypeFoldable, Instance}; use rustc::ty::layout::{TyLayout, HasTyCtxt, FnTypeExt}; use rustc::mir::{self, Body}; -use rustc::session::config::DebugInfo; -use rustc_target::abi::call::{FnType, PassMode, IgnoreMode}; -use rustc_target::abi::{Variants, VariantIdx}; +use rustc_target::abi::call::{FnType, PassMode}; use crate::base; -use crate::debuginfo::{self, VariableAccess, VariableKind, FunctionDebugContext}; use crate::traits::*; -use syntax_pos::{DUMMY_SP, BytePos, Span}; -use syntax::symbol::kw; - use std::iter; -use rustc_data_structures::bit_set::BitSet; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; use self::analyze::CleanupKind; +use self::debuginfo::FunctionDebugContext; use self::place::PlaceRef; use rustc::mir::traversal; @@ -28,9 +23,9 @@ mir: &'a mir::Body<'tcx>, - debug_context: FunctionDebugContext<Bx::DIScope>, + debug_context: Option<FunctionDebugContext<Bx::DIScope>>, - llfn: Bx::Value, + llfn: Bx::Function, cx: &'a Bx::CodegenCx, @@ -79,12 +74,7 @@ /// notably `expect`. locals: IndexVec<mir::Local, LocalRef<'tcx, Bx::Value>>, - /// Debug information for MIR scopes. - scopes: IndexVec<mir::SourceScope, debuginfo::MirDebugScope<Bx::DIScope>>, - - /// If this function is a C-variadic function, this contains the `PlaceRef` of the - /// "spoofed" `VaListImpl`. - va_list_ref: Option<PlaceRef<'tcx, Bx::Value>>, + per_local_var_debug_info: Option<IndexVec<mir::Local, Vec<debuginfo::VarDebugInfo<'tcx>>>>, } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -97,64 +87,6 @@ value, ) } - - pub fn set_debug_loc( - &mut self, - bx: &mut Bx, - source_info: mir::SourceInfo - ) { - let (scope, span) = self.debug_loc(source_info); - bx.set_source_location(&mut self.debug_context, scope, span); - } - - pub fn debug_loc(&self, source_info: mir::SourceInfo) -> (Option<Bx::DIScope>, Span) { - // Bail out if debug info emission is not enabled. - match self.debug_context { - FunctionDebugContext::DebugInfoDisabled | - FunctionDebugContext::FunctionWithoutDebugInfo => { - return (self.scopes[source_info.scope].scope_metadata, source_info.span); - } - FunctionDebugContext::RegularContext(_) =>{} - } - - // In order to have a good line stepping behavior in debugger, we overwrite debug - // locations of macro expansions with that of the outermost expansion site - // (unless the crate is being compiled with `-Z debug-macros`). - if !source_info.span.from_expansion() || - self.cx.sess().opts.debugging_opts.debug_macros { - let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo()); - (scope, source_info.span) - } else { - // Walk up the macro expansion chain until we reach a non-expanded span. - // We also stop at the function body level because no line stepping can occur - // at the level above that. - let span = syntax_pos::hygiene::walk_chain(source_info.span, self.mir.span.ctxt()); - let scope = self.scope_metadata_for_loc(source_info.scope, span.lo()); - // Use span of the outermost expansion site, while keeping the original lexical scope. - (scope, span) - } - } - - // DILocations inherit source file name from the parent DIScope. Due to macro expansions - // it may so happen that the current span belongs to a different file than the DIScope - // corresponding to span's containing source scope. If so, we need to create a DIScope - // "extension" into that file. - fn scope_metadata_for_loc(&self, scope_id: mir::SourceScope, pos: BytePos) - -> Option<Bx::DIScope> { - let scope_metadata = self.scopes[scope_id].scope_metadata; - if pos < self.scopes[scope_id].file_start_pos || - pos >= self.scopes[scope_id].file_end_pos { - let sm = self.cx.sess().source_map(); - let defining_crate = self.debug_context.get_ref(DUMMY_SP).defining_crate; - Some(self.cx.extend_scope_to_file( - scope_metadata.unwrap(), - &sm.lookup_char_pos(pos).file, - defining_crate - )) - } else { - scope_metadata - } - } } enum LocalRef<'tcx, V> { @@ -187,7 +119,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx: &'a Bx::CodegenCx, - llfn: Bx::Value, + llfn: Bx::Function, mir: &'a Body<'tcx>, instance: Instance<'tcx>, sig: ty::FnSig<'tcx>, @@ -196,14 +128,18 @@ let fn_ty = FnType::new(cx, sig, &[]); debug!("fn_ty: {:?}", fn_ty); - let mut debug_context = + + let debug_context = cx.create_function_debug_context(instance, sig, llfn, mir); + let mut bx = Bx::new_block(cx, llfn, "start"); if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) { bx.set_personality_fn(cx.eh_personality()); } + bx.sideeffect(); + let cleanup_kinds = analyze::cleanup_kinds(&mir); // Allocate a `Block` for every basic block, except // the start block, if nothing loops back to it. @@ -217,8 +153,6 @@ } }).collect(); - // Compute debuginfo scopes from MIR scopes. - let scopes = cx.create_mir_scopes(mir, &mut debug_context); let (landing_pads, funclets) = create_funclets(mir, &mut bx, &cleanup_kinds, &block_bxs); let mut fx = FunctionCx { @@ -233,83 +167,38 @@ cleanup_kinds, landing_pads, funclets, - scopes, locals: IndexVec::new(), debug_context, - va_list_ref: None, + per_local_var_debug_info: debuginfo::per_local_var_debug_info(cx.tcx(), mir), }; let memory_locals = analyze::non_ssa_locals(&fx); // Allocate variable and temp allocas fx.locals = { - // FIXME(dlrobertson): This is ugly. Find a better way of getting the `PlaceRef` or - // `LocalRef` from `arg_local_refs` - let mut va_list_ref = None; - let args = arg_local_refs(&mut bx, &fx, &memory_locals, &mut va_list_ref); - fx.va_list_ref = va_list_ref; + let args = arg_local_refs(&mut bx, &fx, &memory_locals); let mut allocate_local = |local| { let decl = &mir.local_decls[local]; let layout = bx.layout_of(fx.monomorphize(&decl.ty)); assert!(!layout.ty.has_erasable_regions()); - if let Some(name) = decl.name { - // User variable - let debug_scope = fx.scopes[decl.visibility_scope]; - let dbg = debug_scope.is_valid() && - bx.sess().opts.debuginfo == DebugInfo::Full; + if local == mir::RETURN_PLACE && fx.fn_ty.ret.is_indirect() { + debug!("alloc: {:?} (return place) -> place", local); + let llretptr = bx.get_param(0); + return LocalRef::Place(PlaceRef::new_sized(llretptr, layout)); + } - if !memory_locals.contains(local) && !dbg { - debug!("alloc: {:?} ({}) -> operand", local, name); - return LocalRef::new_operand(&mut bx, layout); - } - - debug!("alloc: {:?} ({}) -> place", local, name); + if memory_locals.contains(local) { + debug!("alloc: {:?} -> place", local); if layout.is_unsized() { - let indirect_place = - PlaceRef::alloca_unsized_indirect(&mut bx, layout); - bx.set_var_name(indirect_place.llval, name); - // FIXME: add an appropriate debuginfo - LocalRef::UnsizedPlace(indirect_place) + LocalRef::UnsizedPlace(PlaceRef::alloca_unsized_indirect(&mut bx, layout)) } else { - let place = PlaceRef::alloca(&mut bx, layout); - bx.set_var_name(place.llval, name); - if dbg { - let (scope, span) = fx.debug_loc(mir::SourceInfo { - span: decl.source_info.span, - scope: decl.visibility_scope, - }); - bx.declare_local(&fx.debug_context, name, layout.ty, scope.unwrap(), - VariableAccess::DirectVariable { alloca: place.llval }, - VariableKind::LocalVariable, span); - } - LocalRef::Place(place) + LocalRef::Place(PlaceRef::alloca(&mut bx, layout)) } } else { - // Temporary or return place - if local == mir::RETURN_PLACE && fx.fn_ty.ret.is_indirect() { - debug!("alloc: {:?} (return place) -> place", local); - let llretptr = bx.get_param(0); - LocalRef::Place(PlaceRef::new_sized(llretptr, layout)) - } else if memory_locals.contains(local) { - debug!("alloc: {:?} -> place", local); - if layout.is_unsized() { - let indirect_place = PlaceRef::alloca_unsized_indirect(&mut bx, layout); - bx.set_var_name(indirect_place.llval, format_args!("{:?}", local)); - LocalRef::UnsizedPlace(indirect_place) - } else { - let place = PlaceRef::alloca(&mut bx, layout); - bx.set_var_name(place.llval, format_args!("{:?}", local)); - LocalRef::Place(place) - } - } else { - // If this is an immediate local, we do not create an - // alloca in advance. Instead we wait until we see the - // definition and update the operand there. - debug!("alloc: {:?} -> operand", local); - LocalRef::new_operand(&mut bx, layout) - } + debug!("alloc: {:?} -> operand", local); + LocalRef::new_operand(&mut bx, layout) } }; @@ -320,6 +209,9 @@ .collect() }; + // Apply debuginfo to the newly allocated locals. + fx.debug_introduce_locals(&mut bx); + // Branch to the START block, if it's not the entry block. if reentrant_start_block { bx.br(fx.blocks[mir::START_BLOCK]); @@ -328,7 +220,9 @@ // Up until here, IR instructions for this function have explicitly not been annotated with // source code location, so we don't step into call setup code. From here on, source location // emitting should be enabled. - debuginfo::start_emitting_source_locations(&mut fx.debug_context); + if let Some(debug_context) = &mut fx.debug_context { + debug_context.source_locations_enabled = true; + } let rpo = traversal::reverse_postorder(&mir); let mut visited = BitSet::new_empty(mir.basic_blocks().len()); @@ -426,40 +320,14 @@ bx: &mut Bx, fx: &FunctionCx<'a, 'tcx, Bx>, memory_locals: &BitSet<mir::Local>, - va_list_ref: &mut Option<PlaceRef<'tcx, Bx::Value>>, ) -> Vec<LocalRef<'tcx, Bx::Value>> { let mir = fx.mir; - let tcx = fx.cx.tcx(); let mut idx = 0; let mut llarg_idx = fx.fn_ty.ret.is_indirect() as usize; - // Get the argument scope, if it exists and if we need it. - let arg_scope = fx.scopes[mir::OUTERMOST_SOURCE_SCOPE]; - let arg_scope = if bx.sess().opts.debuginfo == DebugInfo::Full { - arg_scope.scope_metadata - } else { - None - }; - - // Store the index of the last argument. This is used to - // call va_start on the va_list instead of attempting - // to store_fn_arg. - let last_arg_idx = if fx.fn_ty.args.is_empty() { - None - } else { - Some(fx.fn_ty.args.len() - 1) - }; - mir.args_iter().enumerate().map(|(arg_index, local)| { let arg_decl = &mir.local_decls[local]; - // FIXME(eddyb) don't allocate a `String` unless it gets used. - let name = if let Some(name) = arg_decl.name { - name.as_str().to_string() - } else { - format!("{:?}", local) - }; - if Some(local) == mir.spread_arg { // This argument (e.g., the last argument in the "rust-call" ABI) // is a tuple that was spread at the ABI level and now we have @@ -467,13 +335,12 @@ // individual LLVM function arguments. let arg_ty = fx.monomorphize(&arg_decl.ty); - let tupled_arg_tys = match arg_ty.sty { + let tupled_arg_tys = match arg_ty.kind { ty::Tuple(ref tys) => tys, _ => bug!("spread argument isn't a tuple?!") }; let place = PlaceRef::alloca(bx, bx.layout_of(arg_ty)); - bx.set_var_name(place.llval, name); for i in 0..tupled_arg_tys.len() { let arg = &fx.fn_ty.args[idx]; idx += 1; @@ -484,44 +351,35 @@ bx.store_fn_arg(arg, &mut llarg_idx, pr_field); } - // Now that we have one alloca that contains the aggregate value, - // we can create one debuginfo entry for the argument. - arg_scope.map(|scope| { - let variable_access = VariableAccess::DirectVariable { - alloca: place.llval - }; - bx.declare_local( - &fx.debug_context, - arg_decl.name.unwrap_or(kw::Invalid), - arg_ty, scope, - variable_access, - VariableKind::ArgumentVariable(arg_index + 1), - DUMMY_SP - ); - }); - return LocalRef::Place(place); } + if fx.fn_ty.c_variadic && arg_index == fx.fn_ty.args.len() { + let arg_ty = fx.monomorphize(&arg_decl.ty); + + let va_list = PlaceRef::alloca(bx, bx.layout_of(arg_ty)); + bx.va_start(va_list.llval); + + return LocalRef::Place(va_list); + } + let arg = &fx.fn_ty.args[idx]; idx += 1; if arg.pad.is_some() { llarg_idx += 1; } - if arg_scope.is_none() && !memory_locals.contains(local) { + if !memory_locals.contains(local) { // We don't have to cast or keep the argument in the alloca. // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead // of putting everything in allocas just so we can use llvm.dbg.declare. let local = |op| LocalRef::Operand(Some(op)); match arg.mode { - PassMode::Ignore(IgnoreMode::Zst) => { + PassMode::Ignore => { return local(OperandRef::new_zst(bx, arg.layout)); } - PassMode::Ignore(IgnoreMode::CVarArgs) => {} PassMode::Direct(_) => { let llarg = bx.get_param(llarg_idx); - bx.set_var_name(llarg, &name); llarg_idx += 1; return local( OperandRef::from_immediate_or_packed_pair(bx, llarg, arg.layout)); @@ -530,11 +388,6 @@ let (a, b) = (bx.get_param(llarg_idx), bx.get_param(llarg_idx + 1)); llarg_idx += 2; - // FIXME(eddyb) these are scalar components, - // maybe extract the high-level fields? - bx.set_var_name(a, format_args!("{}.0", name)); - bx.set_var_name(b, format_args!("{}.1", name)); - return local(OperandRef { val: OperandValue::Pair(a, b), layout: arg.layout @@ -544,14 +397,13 @@ } } - let place = if arg.is_sized_indirect() { + if arg.is_sized_indirect() { // Don't copy an indirect argument to an alloca, the caller // already put it in a temporary alloca and gave it up. // FIXME: lifetimes let llarg = bx.get_param(llarg_idx); - bx.set_var_name(llarg, &name); llarg_idx += 1; - PlaceRef::new_sized(llarg, arg.layout) + LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout)) } else if arg.is_unsized_indirect() { // As the storage for the indirect argument lives during // the whole function call, we just copy the fat pointer. @@ -562,165 +414,12 @@ let indirect_operand = OperandValue::Pair(llarg, llextra); let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout); - bx.set_var_name(tmp.llval, name); indirect_operand.store(bx, tmp); - tmp + LocalRef::UnsizedPlace(tmp) } else { let tmp = PlaceRef::alloca(bx, arg.layout); - bx.set_var_name(tmp.llval, name); - if fx.fn_ty.c_variadic && last_arg_idx.map(|idx| arg_index == idx).unwrap_or(false) { - let va_list_did = match tcx.lang_items().va_list() { - Some(did) => did, - None => bug!("`va_list` lang item required for C-variadic functions"), - }; - match arg_decl.ty.sty { - ty::Adt(def, _) if def.did == va_list_did => { - // Call `va_start` on the spoofed `VaListImpl`. - bx.va_start(tmp.llval); - *va_list_ref = Some(tmp); - }, - _ => bug!("last argument of variadic function is not a `va_list`") - } - } else { - bx.store_fn_arg(arg, &mut llarg_idx, tmp); - } - tmp - }; - let upvar_debuginfo = &mir.__upvar_debuginfo_codegen_only_do_not_use; - arg_scope.map(|scope| { - // Is this a regular argument? - if arg_index > 0 || upvar_debuginfo.is_empty() { - // The Rust ABI passes indirect variables using a pointer and a manual copy, so we - // need to insert a deref here, but the C ABI uses a pointer and a copy using the - // byval attribute, for which LLVM always does the deref itself, - // so we must not add it. - let variable_access = VariableAccess::DirectVariable { - alloca: place.llval - }; - - bx.declare_local( - &fx.debug_context, - arg_decl.name.unwrap_or(kw::Invalid), - arg.layout.ty, - scope, - variable_access, - VariableKind::ArgumentVariable(arg_index + 1), - DUMMY_SP - ); - return; - } - - let pin_did = tcx.lang_items().pin_type(); - // Or is it the closure environment? - let (closure_layout, env_ref) = match arg.layout.ty.sty { - ty::RawPtr(ty::TypeAndMut { ty, .. }) | - ty::Ref(_, ty, _) => (bx.layout_of(ty), true), - ty::Adt(def, substs) if Some(def.did) == pin_did => { - match substs.type_at(0).sty { - ty::Ref(_, ty, _) => (bx.layout_of(ty), true), - _ => (arg.layout, false), - } - } - _ => (arg.layout, false) - }; - - let (def_id, upvar_substs) = match closure_layout.ty.sty { - ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), - ty::Generator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), - _ => bug!("upvar debuginfo with non-closure arg0 type `{}`", closure_layout.ty) - }; - let upvar_tys = upvar_substs.upvar_tys(def_id, tcx); - - let extra_locals = { - let upvars = upvar_debuginfo - .iter() - .zip(upvar_tys) - .enumerate() - .map(|(i, (upvar, ty))| { - (None, i, upvar.debug_name, upvar.by_ref, ty, scope, DUMMY_SP) - }); - - let generator_fields = mir.generator_layout.as_ref().map(|generator_layout| { - let (def_id, gen_substs) = match closure_layout.ty.sty { - ty::Generator(def_id, substs, _) => (def_id, substs), - _ => bug!("generator layout without generator substs"), - }; - let state_tys = gen_substs.state_tys(def_id, tcx); - - generator_layout.variant_fields.iter() - .zip(state_tys) - .enumerate() - .flat_map(move |(variant_idx, (fields, tys))| { - let variant_idx = Some(VariantIdx::from(variant_idx)); - fields.iter() - .zip(tys) - .enumerate() - .filter_map(move |(i, (field, ty))| { - let decl = &generator_layout. - __local_debuginfo_codegen_only_do_not_use[*field]; - if let Some(name) = decl.name { - let ty = fx.monomorphize(&ty); - let (var_scope, var_span) = fx.debug_loc(mir::SourceInfo { - span: decl.source_info.span, - scope: decl.visibility_scope, - }); - let var_scope = var_scope.unwrap_or(scope); - Some((variant_idx, i, name, false, ty, var_scope, var_span)) - } else { - None - } - }) - }) - }).into_iter().flatten(); - - upvars.chain(generator_fields) - }; - - for (variant_idx, field, name, by_ref, ty, var_scope, var_span) in extra_locals { - let fields = match variant_idx { - Some(variant_idx) => { - match &closure_layout.variants { - Variants::Multiple { variants, .. } => { - &variants[variant_idx].fields - }, - _ => bug!("variant index on univariant layout"), - } - } - None => &closure_layout.fields, - }; - let byte_offset_of_var_in_env = fields.offset(field).bytes(); - - let ops = bx.debuginfo_upvar_ops_sequence(byte_offset_of_var_in_env); - - // The environment and the capture can each be indirect. - let mut ops = if env_ref { &ops[..] } else { &ops[1..] }; - - let ty = if let (true, &ty::Ref(_, ty, _)) = (by_ref, &ty.sty) { - ty - } else { - ops = &ops[..ops.len() - 1]; - ty - }; - - let variable_access = VariableAccess::IndirectVariable { - alloca: place.llval, - address_operations: &ops - }; - bx.declare_local( - &fx.debug_context, - name, - ty, - var_scope, - variable_access, - VariableKind::LocalVariable, - var_span - ); - } - }); - if arg.is_unsized_indirect() { - LocalRef::UnsizedPlace(place) - } else { - LocalRef::Place(place) + bx.store_fn_arg(arg, &mut llarg_idx, tmp); + LocalRef::Place(tmp) } }).collect() } @@ -728,6 +427,7 @@ mod analyze; mod block; pub mod constant; +pub mod debuginfo; pub mod place; pub mod operand; mod rvalue;
diff --git a/src/librustc_codegen_ssa/mir/operand.rs b/src/librustc_codegen_ssa/mir/operand.rs index daa25b2..ba5e47a 100644 --- a/src/librustc_codegen_ssa/mir/operand.rs +++ b/src/librustc_codegen_ssa/mir/operand.rs
@@ -79,6 +79,7 @@ ConstValue::Unevaluated(..) => bug!("unevaluated constant in `OperandRef::from_const`"), ConstValue::Param(_) => bug!("encountered a ConstValue::Param in codegen"), ConstValue::Infer(_) => bug!("encountered a ConstValue::Infer in codegen"), + ConstValue::Bound(..) => bug!("encountered a ConstValue::Bound in codegen"), ConstValue::Placeholder(_) => bug!("encountered a ConstValue::Placeholder in codegen"), ConstValue::Scalar(x) => { let scalar = match layout.abi {
diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs index a4b4cb5..3e7c4ef 100644 --- a/src/librustc_codegen_ssa/mir/place.rs +++ b/src/librustc_codegen_ssa/mir/place.rs
@@ -68,6 +68,8 @@ } } + // FIXME(eddyb) pass something else for the name so no work is done + // unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`). pub fn alloca<Bx: BuilderMethods<'a, 'tcx, Value = V>>( bx: &mut Bx, layout: TyLayout<'tcx>, @@ -78,6 +80,8 @@ } /// Returns a place for an indirect reference to an unsized place. + // FIXME(eddyb) pass something else for the name so no work is done + // unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`). pub fn alloca_unsized_indirect<Bx: BuilderMethods<'a, 'tcx, Value = V>>( bx: &mut Bx, layout: TyLayout<'tcx>, @@ -144,7 +148,7 @@ // * no metadata available - just log the case // * known alignment - sized types, `[T]`, `str` or a foreign type // * packed struct - there is no alignment padding - match field.ty.sty { + match field.ty.kind { _ if self.llextra.is_none() => { debug!("unsized field `{}`, of `{:?}` has no metadata for adjustment", ix, self.llval); @@ -394,8 +398,8 @@ // Statically compute the offset if we can, otherwise just use the element size, // as this will yield the lowest alignment. let layout = self.layout.field(bx, 0); - let offset = if bx.is_const_integral(llindex) { - layout.size.checked_mul(bx.const_to_uint(llindex), bx).unwrap_or(layout.size) + let offset = if let Some(llindex) = bx.const_to_opt_uint(llindex) { + layout.size.checked_mul(llindex, bx).unwrap_or(layout.size) } else { layout.size };
diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index f21836a..1608f22 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs
@@ -10,7 +10,6 @@ use crate::base; use crate::MemFlags; -use crate::callee; use crate::common::{self, RealPredicate, IntPredicate}; use crate::traits::*; @@ -95,7 +94,7 @@ let size = bx.const_usize(dest.layout.size.bytes()); // Use llvm.memset.p0i8.* to initialize all zero arrays - if bx.cx().is_const_integral(v) && bx.cx().const_to_uint(v) == 0 { + if bx.cx().const_to_opt_uint(v) == Some(0) { let fill = bx.cx().const_u8(0); bx.memset(start, fill, size, dest.align, MemFlags::empty()); return bx; @@ -184,13 +183,21 @@ let val = match *kind { mir::CastKind::Pointer(PointerCast::ReifyFnPointer) => { - match operand.layout.ty.sty { + match operand.layout.ty.kind { ty::FnDef(def_id, substs) => { if bx.cx().tcx().has_attr(def_id, sym::rustc_args_required_const) { bug!("reifying a fn ptr that requires const arguments"); } OperandValue::Immediate( - callee::resolve_and_get_fn(bx.cx(), def_id, substs)) + bx.get_fn_addr( + ty::Instance::resolve_for_fn_ptr( + bx.tcx(), + ty::ParamEnv::reveal_all(), + def_id, + substs + ).unwrap() + ) + ) } _ => { bug!("{} cannot be reified to a fn ptr", operand.layout.ty) @@ -198,11 +205,14 @@ } } mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)) => { - match operand.layout.ty.sty { + match operand.layout.ty.kind { ty::Closure(def_id, substs) => { let instance = Instance::resolve_closure( - bx.cx().tcx(), def_id, substs, ty::ClosureKind::FnOnce); - OperandValue::Immediate(bx.cx().get_fn(instance)) + bx.cx().tcx(), + def_id, + substs, + ty::ClosureKind::FnOnce); + OperandValue::Immediate(bx.cx().get_fn_addr(instance)) } _ => { bug!("{} cannot be cast to a fn ptr", operand.layout.ty) @@ -485,7 +495,7 @@ } }; let instance = ty::Instance::mono(bx.tcx(), def_id); - let r = bx.cx().get_fn(instance); + let r = bx.cx().get_fn_addr(instance); let call = bx.call(r, &[llsize, llalign], None); let val = bx.pointercast(call, llty_ptr); @@ -520,12 +530,9 @@ ) -> Bx::Value { // ZST are passed as operands and require special handling // because codegen_place() panics if Local is operand. - if let mir::Place { - base: mir::PlaceBase::Local(index), - projection: box [], - } = *place { + if let Some(index) = place.as_local() { if let LocalRef::Operand(Some(op)) = self.locals[index] { - if let ty::Array(_, n) = op.layout.ty.sty { + if let ty::Array(_, n) = op.layout.ty.kind { let n = n.eval_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all()); return bx.cx().const_usize(n); } @@ -546,7 +553,6 @@ ) -> Bx::Value { let is_float = input_ty.is_floating_point(); let is_signed = input_ty.is_signed(); - let is_unit = input_ty.is_unit(); match op { mir::BinOp::Add => if is_float { bx.fadd(lhs, rhs) @@ -584,13 +590,7 @@ mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs), mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs), mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt | - mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_unit { - bx.cx().const_bool(match op { - mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt => false, - mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => true, - _ => unreachable!() - }) - } else if is_float { + mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_float { bx.fcmp( base::bin_op_to_fcmp_predicate(op.to_hir_binop()), lhs, rhs
diff --git a/src/librustc_codegen_ssa/mir/statement.rs b/src/librustc_codegen_ssa/mir/statement.rs index dab7dfc..d11601b 100644 --- a/src/librustc_codegen_ssa/mir/statement.rs +++ b/src/librustc_codegen_ssa/mir/statement.rs
@@ -17,11 +17,8 @@ self.set_debug_loc(&mut bx, statement.source_info); match statement.kind { mir::StatementKind::Assign(box(ref place, ref rvalue)) => { - if let mir::Place { - base: mir::PlaceBase::Local(index), - projection: box [], - } = place { - match self.locals[*index] { + if let Some(index) = place.as_local() { + match self.locals[index] { LocalRef::Place(cg_dest) => { self.codegen_rvalue(bx, cg_dest, rvalue) } @@ -30,21 +27,8 @@ } LocalRef::Operand(None) => { let (mut bx, operand) = self.codegen_rvalue_operand(bx, rvalue); - if let Some(name) = self.mir.local_decls[*index].name { - match operand.val { - OperandValue::Ref(x, ..) | - OperandValue::Immediate(x) => { - bx.set_var_name(x, name); - } - OperandValue::Pair(a, b) => { - // FIXME(eddyb) these are scalar components, - // maybe extract the high-level fields? - bx.set_var_name(a, format_args!("{}.0", name)); - bx.set_var_name(b, format_args!("{}.1", name)); - } - } - } - self.locals[*index] = LocalRef::Operand(Some(operand)); + self.locals[index] = LocalRef::Operand(Some(operand)); + self.debug_introduce_local(&mut bx, index); bx } LocalRef::Operand(Some(op)) => {
diff --git a/src/librustc_codegen_ssa/mono_item.rs b/src/librustc_codegen_ssa/mono_item.rs index 5801963..10177d2 100644 --- a/src/librustc_codegen_ssa/mono_item.rs +++ b/src/librustc_codegen_ssa/mono_item.rs
@@ -30,7 +30,7 @@ } MonoItem::GlobalAsm(hir_id) => { let item = cx.tcx().hir().expect_item(hir_id); - if let hir::ItemKind::GlobalAsm(ref ga) = item.node { + if let hir::ItemKind::GlobalAsm(ref ga) = item.kind { cx.codegen_global_asm(ga); } else { span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type")
diff --git a/src/librustc_codegen_ssa/traits/backend.rs b/src/librustc_codegen_ssa/traits/backend.rs index 9fbb44d..8ab8243 100644 --- a/src/librustc_codegen_ssa/traits/backend.rs +++ b/src/librustc_codegen_ssa/traits/backend.rs
@@ -8,11 +8,14 @@ use rustc::ty::TyCtxt; use rustc_codegen_utils::codegen_backend::CodegenBackend; use std::sync::Arc; -use syntax::ext::allocator::AllocatorKind; -use syntax_pos::symbol::InternedString; +use std::sync::mpsc; +use syntax::expand::allocator::AllocatorKind; +use syntax_pos::symbol::Symbol; pub trait BackendTypes { type Value: CodegenObject; + type Function: CodegenObject; + type BasicBlock: Copy; type Type: CodegenObject; type Funclet; @@ -44,7 +47,12 @@ mods: &mut Self::Module, kind: AllocatorKind, ); - fn compile_codegen_unit(&self, tcx: TyCtxt<'_>, cgu_name: InternedString); + fn compile_codegen_unit( + &self, + tcx: TyCtxt<'_>, + cgu_name: Symbol, + tx_to_llvm_workers: &mpsc::Sender<Box<dyn std::any::Any + Send>>, + ); // If find_features is true this won't access `sess.crate_types` by assuming // that `is_pie_binary` is false. When we discover LLVM target features // `sess.crate_types` is uninitialized so we cannot access it.
diff --git a/src/librustc_codegen_ssa/traits/builder.rs b/src/librustc_codegen_ssa/traits/builder.rs index 1886701..62b5bcb 100644 --- a/src/librustc_codegen_ssa/traits/builder.rs +++ b/src/librustc_codegen_ssa/traits/builder.rs
@@ -34,7 +34,7 @@ + HasTargetSpec { - fn new_block<'b>(cx: &'a Self::CodegenCx, llfn: Self::Value, name: &'b str) -> Self; + fn new_block<'b>(cx: &'a Self::CodegenCx, llfn: Self::Function, name: &'b str) -> Self; fn with_cx(cx: &'a Self::CodegenCx) -> Self; fn build_sibling_block(&self, name: &str) -> Self; fn cx(&self) -> &Self::CodegenCx;
diff --git a/src/librustc_codegen_ssa/traits/consts.rs b/src/librustc_codegen_ssa/traits/consts.rs index e7ce03f..8c462e7 100644 --- a/src/librustc_codegen_ssa/traits/consts.rs +++ b/src/librustc_codegen_ssa/traits/consts.rs
@@ -3,6 +3,7 @@ use rustc::mir::interpret::Allocation; use rustc::mir::interpret::Scalar; use rustc::ty::layout; +use syntax_pos::Symbol; pub trait ConstMethods<'tcx>: BackendTypes { // Constant constructors @@ -19,13 +20,12 @@ fn const_u8(&self, i: u8) -> Self::Value; fn const_real(&self, t: Self::Type, val: f64) -> Self::Value; + fn const_str(&self, s: Symbol) -> (Self::Value, Self::Value); fn const_struct(&self, elts: &[Self::Value], packed: bool) -> Self::Value; - fn const_to_uint(&self, v: Self::Value) -> u64; + fn const_to_opt_uint(&self, v: Self::Value) -> Option<u64>; fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option<u128>; - fn is_const_integral(&self, v: Self::Value) -> bool; - fn scalar_to_backend( &self, cv: Scalar,
diff --git a/src/librustc_codegen_ssa/traits/debuginfo.rs b/src/librustc_codegen_ssa/traits/debuginfo.rs index 9c16b86..802eaaa 100644 --- a/src/librustc_codegen_ssa/traits/debuginfo.rs +++ b/src/librustc_codegen_ssa/traits/debuginfo.rs
@@ -1,9 +1,9 @@ use super::BackendTypes; -use crate::debuginfo::{FunctionDebugContext, MirDebugScope, VariableAccess, VariableKind}; +use crate::mir::debuginfo::{FunctionDebugContext, VariableKind}; use rustc::hir::def_id::CrateNum; use rustc::mir; use rustc::ty::{self, Ty, Instance}; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc::ty::layout::Size; use syntax::ast::Name; use syntax_pos::{SourceFile, Span}; @@ -13,22 +13,15 @@ /// Creates the function-specific debug context. /// /// Returns the FunctionDebugContext for the function which holds state needed - /// for debug info creation. The function may also return another variant of the - /// FunctionDebugContext enum which indicates why no debuginfo should be created - /// for the function. + /// for debug info creation, if it is enabled. fn create_function_debug_context( &self, instance: Instance<'tcx>, sig: ty::FnSig<'tcx>, - llfn: Self::Value, + llfn: Self::Function, mir: &mir::Body<'_>, - ) -> FunctionDebugContext<Self::DIScope>; + ) -> Option<FunctionDebugContext<Self::DIScope>>; - fn create_mir_scopes( - &self, - mir: &mir::Body<'_>, - debug_context: &mut FunctionDebugContext<Self::DIScope>, - ) -> IndexVec<mir::SourceScope, MirDebugScope<Self::DIScope>>; fn extend_scope_to_file( &self, scope_metadata: Self::DIScope, @@ -36,7 +29,6 @@ defining_crate: CrateNum, ) -> Self::DIScope; fn debuginfo_finalize(&self); - fn debuginfo_upvar_ops_sequence(&self, byte_offset_of_var_in_env: u64) -> [i64; 4]; } pub trait DebugInfoBuilderMethods<'tcx>: BackendTypes { @@ -46,16 +38,19 @@ variable_name: Name, variable_type: Ty<'tcx>, scope_metadata: Self::DIScope, - variable_access: VariableAccess<'_, Self::Value>, + variable_alloca: Self::Value, + direct_offset: Size, + // NB: each offset implies a deref (i.e. they're steps in a pointer chain). + indirect_offsets: &[Size], variable_kind: VariableKind, span: Span, ); fn set_source_location( &mut self, debug_context: &mut FunctionDebugContext<Self::DIScope>, - scope: Option<Self::DIScope>, + scope: Self::DIScope, span: Span, ); fn insert_reference_to_gdb_debug_scripts_section_global(&mut self); - fn set_var_name(&mut self, value: Self::Value, name: impl ToString); + fn set_var_name(&mut self, value: Self::Value, name: &str); }
diff --git a/src/librustc_codegen_ssa/traits/declare.rs b/src/librustc_codegen_ssa/traits/declare.rs index 624a982..cd42044 100644 --- a/src/librustc_codegen_ssa/traits/declare.rs +++ b/src/librustc_codegen_ssa/traits/declare.rs
@@ -17,13 +17,13 @@ /// /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. - fn declare_cfn(&self, name: &str, fn_type: Self::Type) -> Self::Value; + fn declare_cfn(&self, name: &str, fn_type: Self::Type) -> Self::Function; /// Declare a Rust function. /// /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. - fn declare_fn(&self, name: &str, sig: ty::PolyFnSig<'tcx>) -> Self::Value; + fn declare_fn(&self, name: &str, sig: ty::PolyFnSig<'tcx>) -> Self::Function; /// Declare a global with an intention to define it. ///
diff --git a/src/librustc_codegen_ssa/traits/intrinsic.rs b/src/librustc_codegen_ssa/traits/intrinsic.rs index 7c79cd6..2c48408 100644 --- a/src/librustc_codegen_ssa/traits/intrinsic.rs +++ b/src/librustc_codegen_ssa/traits/intrinsic.rs
@@ -20,6 +20,7 @@ fn abort(&mut self); fn assume(&mut self, val: Self::Value); fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value; + fn sideeffect(&mut self); /// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in /// Rust defined C-variadic functions. fn va_start(&mut self, val: Self::Value) -> Self::Value;
diff --git a/src/librustc_codegen_ssa/traits/misc.rs b/src/librustc_codegen_ssa/traits/misc.rs index 46c88a6..658ddd0 100644 --- a/src/librustc_codegen_ssa/traits/misc.rs +++ b/src/librustc_codegen_ssa/traits/misc.rs
@@ -11,14 +11,14 @@ &self, ) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), Self::Value>>; fn check_overflow(&self) -> bool; - fn instances(&self) -> &RefCell<FxHashMap<Instance<'tcx>, Self::Value>>; - fn get_fn(&self, instance: Instance<'tcx>) -> Self::Value; + fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function; + fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value; fn eh_personality(&self) -> Self::Value; fn eh_unwind_resume(&self) -> Self::Value; fn sess(&self) -> &Session; fn codegen_unit(&self) -> &Arc<CodegenUnit<'tcx>>; fn used_statics(&self) -> &RefCell<Vec<Self::Value>>; - fn set_frame_pointer_elimination(&self, llfn: Self::Value); - fn apply_target_cpu_attr(&self, llfn: Self::Value); + fn set_frame_pointer_elimination(&self, llfn: Self::Function); + fn apply_target_cpu_attr(&self, llfn: Self::Function); fn create_used_variable(&self); }
diff --git a/src/librustc_codegen_ssa/traits/mod.rs b/src/librustc_codegen_ssa/traits/mod.rs index efe4a25..4318ef1 100644 --- a/src/librustc_codegen_ssa/traits/mod.rs +++ b/src/librustc_codegen_ssa/traits/mod.rs
@@ -88,6 +88,7 @@ type CodegenCx: CodegenMethods<'tcx> + BackendTypes< Value = Self::Value, + Function = Self::Function, BasicBlock = Self::BasicBlock, Type = Self::Type, Funclet = Self::Funclet,
diff --git a/src/librustc_codegen_ssa/traits/statics.rs b/src/librustc_codegen_ssa/traits/statics.rs index 73c4c05..5c108f9 100644 --- a/src/librustc_codegen_ssa/traits/statics.rs +++ b/src/librustc_codegen_ssa/traits/statics.rs
@@ -1,5 +1,4 @@ use super::BackendTypes; -use syntax_pos::symbol::Symbol; use rustc::hir::def_id::DefId; use rustc::ty::layout::Align; @@ -10,12 +9,4 @@ pub trait StaticBuilderMethods: BackendTypes { fn get_static(&mut self, def_id: DefId) -> Self::Value; - fn static_panic_msg( - &mut self, - msg: Option<Symbol>, - filename: Symbol, - line: Self::Value, - col: Self::Value, - kind: &str, - ) -> Self::Value; }
diff --git a/src/librustc_codegen_ssa/traits/type_.rs b/src/librustc_codegen_ssa/traits/type_.rs index 13f72e2..19d41c6 100644 --- a/src/librustc_codegen_ssa/traits/type_.rs +++ b/src/librustc_codegen_ssa/traits/type_.rs
@@ -83,7 +83,7 @@ } let tail = self.tcx().struct_tail_erasing_lifetimes(ty, param_env); - match tail.sty { + match tail.kind { ty::Foreign(..) => false, ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, _ => bug!("unexpected unsized tail: {:?}", tail),
diff --git a/src/librustc_codegen_utils/Cargo.toml b/src/librustc_codegen_utils/Cargo.toml index 89b50c5..c8c219d 100644 --- a/src/librustc_codegen_utils/Cargo.toml +++ b/src/librustc_codegen_utils/Cargo.toml
@@ -10,7 +10,6 @@ test = false [dependencies] -flate2 = "1.0" log = "0.4" punycode = "0.4.0" rustc-demangle = "0.1.16"
diff --git a/src/librustc_codegen_utils/codegen_backend.rs b/src/librustc_codegen_utils/codegen_backend.rs index 262cfb1..0e2c373 100644 --- a/src/librustc_codegen_utils/codegen_backend.rs +++ b/src/librustc_codegen_utils/codegen_backend.rs
@@ -7,7 +7,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] use std::any::Any; -use std::sync::mpsc; use syntax::symbol::Symbol; use rustc::session::Session; @@ -15,7 +14,7 @@ use rustc::session::config::{OutputFilenames, PrintRequest}; use rustc::ty::TyCtxt; use rustc::ty::query::Providers; -use rustc::middle::cstore::{EncodedMetadata, MetadataLoader}; +use rustc::middle::cstore::{EncodedMetadata, MetadataLoaderDyn}; use rustc::dep_graph::DepGraph; pub use rustc_data_structures::sync::MetadataRef; @@ -26,9 +25,8 @@ fn target_features(&self, _sess: &Session) -> Vec<Symbol> { vec![] } fn print_passes(&self) {} fn print_version(&self) {} - fn diagnostics(&self) -> &[(&'static str, &'static str)] { &[] } - fn metadata_loader(&self) -> Box<dyn MetadataLoader + Sync>; + fn metadata_loader(&self) -> Box<MetadataLoaderDyn>; fn provide(&self, _providers: &mut Providers<'_>); fn provide_extern(&self, _providers: &mut Providers<'_>); fn codegen_crate<'tcx>( @@ -36,7 +34,6 @@ tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool, - rx: mpsc::Receiver<Box<dyn Any + Send>>, ) -> Box<dyn Any>; /// This is called on the returned `Box<dyn Any>` from `codegen_backend`
diff --git a/src/librustc_codegen_utils/lib.rs b/src/librustc_codegen_utils/lib.rs index 1201446..6692034 100644 --- a/src/librustc_codegen_utils/lib.rs +++ b/src/librustc_codegen_utils/lib.rs
@@ -18,6 +18,7 @@ extern crate rustc; use rustc::ty::TyCtxt; +use rustc::ty::query::Providers; use rustc::hir::def_id::LOCAL_CRATE; use syntax::symbol::sym; @@ -37,3 +38,7 @@ } } } + +pub fn provide(providers: &mut Providers<'_>) { + crate::symbol_names::provide(providers); +}
diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs index 7ccd024..c52c6cf 100644 --- a/src/librustc_codegen_utils/symbol_names.rs +++ b/src/librustc_codegen_utils/symbol_names.rs
@@ -95,7 +95,7 @@ use rustc::ty::{self, TyCtxt, Instance}; use rustc::mir::mono::{MonoItem, InstantiationMode}; -use syntax_pos::symbol::InternedString; +use syntax_pos::symbol::Symbol; use log::debug; @@ -112,7 +112,7 @@ }; } -fn symbol_name(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> InternedString { +fn symbol_name(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Symbol { let def_id = instance.def_id(); let substs = instance.substs; @@ -123,13 +123,11 @@ if def_id.is_local() { if tcx.plugin_registrar_fn(LOCAL_CRATE) == Some(def_id) { let disambiguator = tcx.sess.local_crate_disambiguator(); - return - InternedString::intern(&tcx.sess.generate_plugin_registrar_symbol(disambiguator)); + return Symbol::intern(&tcx.sess.generate_plugin_registrar_symbol(disambiguator)); } if tcx.proc_macro_decls_static(LOCAL_CRATE) == Some(def_id) { let disambiguator = tcx.sess.local_crate_disambiguator(); - return - InternedString::intern(&tcx.sess.generate_proc_macro_decls_symbol(disambiguator)); + return Symbol::intern(&tcx.sess.generate_proc_macro_decls_symbol(disambiguator)); } } @@ -146,23 +144,22 @@ let attrs = tcx.codegen_fn_attrs(def_id); if is_foreign { if let Some(name) = attrs.link_name { - return name.as_interned_str(); + return name; } // Don't mangle foreign items. - return tcx.item_name(def_id).as_interned_str(); + return tcx.item_name(def_id); } - if let Some(name) = &attrs.export_name { + if let Some(name) = attrs.export_name { // Use provided name - return name.as_interned_str(); + return name; } if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { // Don't mangle - return tcx.item_name(def_id).as_interned_str(); + return tcx.item_name(def_id); } - let is_generic = substs.non_erasable_generics().next().is_some(); let avoid_cross_crate_conflicts = // If this is an instance of a generic function, we also hash in @@ -222,5 +219,5 @@ SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate), }; - InternedString::intern(&mangled) + Symbol::intern(&mangled) }
diff --git a/src/librustc_codegen_utils/symbol_names/legacy.rs b/src/librustc_codegen_utils/symbol_names/legacy.rs index 22b7e0a..601a33a 100644 --- a/src/librustc_codegen_utils/symbol_names/legacy.rs +++ b/src/librustc_codegen_utils/symbol_names/legacy.rs
@@ -3,7 +3,7 @@ use rustc::ich::NodeIdHashingMode; use rustc::mir::interpret::{ConstValue, Scalar}; use rustc::ty::print::{PrettyPrinter, Printer, Print}; -use rustc::ty::subst::{Kind, UnpackedKind}; +use rustc::ty::subst::{GenericArg, GenericArgKind}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Instance}; use rustc::util::common::record_time; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -89,7 +89,7 @@ def_id, substs ); - let mut hasher = StableHasher::<u64>::new(); + let mut hasher = StableHasher::new(); let mut hcx = tcx.create_stable_hashing_context(); record_time(&tcx.sess.perf_stats.symbol_hash_time, || { @@ -111,7 +111,7 @@ // If this is a function, we hash the signature as well. // This is not *strictly* needed, but it may help in some // situations, see the `run-make/a-b-a-linker-guard` test. - if let ty::FnDef(..) = item_type.sty { + if let ty::FnDef(..) = item_type.kind { item_type.fn_sig(tcx).hash_stable(&mut hcx, &mut hasher); } @@ -132,7 +132,7 @@ }); // 64 bits should be enough to avoid collisions. - hasher.finish() + hasher.finish::<u64>() } // Follow C++ namespace-mangling style, see @@ -218,14 +218,14 @@ self, ty: Ty<'tcx>, ) -> Result<Self::Type, Self::Error> { - match ty.sty { + match ty.kind { // Print all nominal types as paths (unlike `pretty_print_type`). ty::FnDef(def_id, substs) | ty::Opaque(def_id, substs) | ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs }) | ty::UnnormalizedProjection(ty::ProjectionTy { item_def_id: def_id, substs }) | - ty::Closure(def_id, ty::ClosureSubsts { substs }) | - ty::Generator(def_id, ty::GeneratorSubsts { substs }, _) => { + ty::Closure(def_id, substs) | + ty::Generator(def_id, substs, _) => { self.print_def_path(def_id, substs) } _ => self.pretty_print_type(ty), @@ -275,7 +275,7 @@ ) -> Result<Self::Path, Self::Error> { // Similar to `pretty_path_qualified`, but for the other // types that are printed as paths (see `print_type` above). - match self_ty.sty { + match self_ty.kind { ty::FnDef(..) | ty::Opaque(..) | ty::Projection(_) | @@ -335,19 +335,19 @@ self.path.finalize_pending_component(); } - self.write_str(&disambiguated_data.data.as_interned_str().as_str())?; + self.write_str(&disambiguated_data.data.as_symbol().as_str())?; Ok(self) } fn path_generic_args( mut self, print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, - args: &[Kind<'tcx>], + args: &[GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { self = print_prefix(self)?; let args = args.iter().cloned().filter(|arg| { match arg.unpack() { - UnpackedKind::Lifetime(_) => false, + GenericArgKind::Lifetime(_) => false, _ => true, } });
diff --git a/src/librustc_codegen_utils/symbol_names/v0.rs b/src/librustc_codegen_utils/symbol_names/v0.rs index 8d6a1d7..55b148f 100644 --- a/src/librustc_codegen_utils/symbol_names/v0.rs +++ b/src/librustc_codegen_utils/symbol_names/v0.rs
@@ -3,7 +3,7 @@ use rustc::hir::map::{DefPathData, DisambiguatedDefPathData}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Instance}; use rustc::ty::print::{Printer, Print}; -use rustc::ty::subst::{Kind, Subst, UnpackedKind}; +use rustc::ty::subst::{GenericArg, Subst, GenericArgKind}; use rustc_data_structures::base_n; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_target::spec::abi::Abi; @@ -56,7 +56,7 @@ start_offset: usize, // The values are start positions in `out`, in bytes. - paths: FxHashMap<(DefId, &'tcx [Kind<'tcx>]), usize>, + paths: FxHashMap<(DefId, &'tcx [GenericArg<'tcx>]), usize>, types: FxHashMap<Ty<'tcx>, usize>, consts: FxHashMap<&'tcx ty::Const<'tcx>, usize>, } @@ -234,7 +234,7 @@ fn print_def_path( mut self, def_id: DefId, - substs: &'tcx [Kind<'tcx>], + substs: &'tcx [GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { if let Some(&i) = self.compress.as_ref().and_then(|c| c.paths.get(&(def_id, substs))) { return self.print_backref(i); @@ -256,7 +256,7 @@ fn print_impl_path( self, impl_def_id: DefId, - substs: &'tcx [Kind<'tcx>], + substs: &'tcx [GenericArg<'tcx>], mut self_ty: Ty<'tcx>, mut impl_trait_ref: Option<ty::TraitRef<'tcx>>, ) -> Result<Self::Path, Self::Error> { @@ -323,7 +323,7 @@ ty: Ty<'tcx>, ) -> Result<Self::Type, Self::Error> { // Basic types, never cached (single-character). - let basic_type = match ty.sty { + let basic_type = match ty.kind { ty::Bool => "b", ty::Char => "c", ty::Str => "e", @@ -360,7 +360,7 @@ } let start = self.out.len(); - match ty.sty { + match ty.kind { // Basic types, handled above. ty::Bool | ty::Char | ty::Str | ty::Int(_) | ty::Uint(_) | ty::Float(_) | @@ -414,8 +414,8 @@ ty::Opaque(def_id, substs) | ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs }) | ty::UnnormalizedProjection(ty::ProjectionTy { item_def_id: def_id, substs }) | - ty::Closure(def_id, ty::ClosureSubsts { substs }) | - ty::Generator(def_id, ty::GeneratorSubsts { substs }, _) => { + ty::Closure(def_id, substs) | + ty::Generator(def_id, substs, _) => { self = self.print_def_path(def_id, substs)?; } ty::Foreign(def_id) => { @@ -511,7 +511,7 @@ } let start = self.out.len(); - match ct.ty.sty { + match ct.ty.kind { ty::Uint(_) => {} _ => { bug!("symbol_names: unsupported constant of type `{}` ({:?})", @@ -619,18 +619,18 @@ fn path_generic_args( mut self, print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, - args: &[Kind<'tcx>], + args: &[GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { // Don't print any regions if they're all erased. let print_regions = args.iter().any(|arg| { match arg.unpack() { - UnpackedKind::Lifetime(r) => *r != ty::ReErased, + GenericArgKind::Lifetime(r) => *r != ty::ReErased, _ => false, } }); let args = args.iter().cloned().filter(|arg| { match arg.unpack() { - UnpackedKind::Lifetime(_) => print_regions, + GenericArgKind::Lifetime(_) => print_regions, _ => true, } }); @@ -643,13 +643,13 @@ self = print_prefix(self)?; for arg in args { match arg.unpack() { - UnpackedKind::Lifetime(lt) => { + GenericArgKind::Lifetime(lt) => { self = lt.print(self)?; } - UnpackedKind::Type(ty) => { + GenericArgKind::Type(ty) => { self = ty.print(self)?; } - UnpackedKind::Const(c) => { + GenericArgKind::Const(c) => { self.push("K"); // FIXME(const_generics) implement `ty::print::Print` on `ty::Const`. // self = c.print(self)?;
diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index be9f79c..065c843 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml
@@ -10,7 +10,7 @@ doctest = false [dependencies] -ena = "0.13" +ena = "0.13.1" indexmap = "1" log = "0.4" jobserver_crate = { version = "0.1.13", package = "jobserver" } @@ -20,10 +20,11 @@ cfg-if = "0.1.2" crossbeam-utils = { version = "0.6.5", features = ["nightly"] } stable_deref_trait = "1.0.0" -rayon = { version = "0.2.0", package = "rustc-rayon" } -rayon-core = { version = "0.2.0", package = "rustc-rayon-core" } +rayon = { version = "0.3.0", package = "rustc-rayon" } +rayon-core = { version = "0.3.0", package = "rustc-rayon-core" } rustc-hash = "1.0.1" smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } +rustc_index = { path = "../librustc_index", package = "rustc_index" } [dependencies.parking_lot] version = "0.9"
diff --git a/src/librustc_data_structures/bit_set.rs b/src/librustc_data_structures/bit_set.rs deleted file mode 100644 index fe8ef64..0000000 --- a/src/librustc_data_structures/bit_set.rs +++ /dev/null
@@ -1,984 +0,0 @@ -use crate::indexed_vec::{Idx, IndexVec}; -use smallvec::SmallVec; -use std::fmt; -use std::iter; -use std::marker::PhantomData; -use std::mem; -use std::slice; - -#[cfg(test)] -mod tests; - -pub type Word = u64; -pub const WORD_BYTES: usize = mem::size_of::<Word>(); -pub const WORD_BITS: usize = WORD_BYTES * 8; - -/// A fixed-size bitset type with a dense representation. It does not support -/// resizing after creation; use `GrowableBitSet` for that. -/// -/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also -/// just be `usize`. -/// -/// All operations that involve an element will panic if the element is equal -/// to or greater than the domain size. All operations that involve two bitsets -/// will panic if the bitsets have differing domain sizes. -#[derive(Clone, Eq, PartialEq, RustcDecodable, RustcEncodable)] -pub struct BitSet<T: Idx> { - domain_size: usize, - words: Vec<Word>, - marker: PhantomData<T>, -} - -impl<T: Idx> BitSet<T> { - /// Creates a new, empty bitset with a given `domain_size`. - #[inline] - pub fn new_empty(domain_size: usize) -> BitSet<T> { - let num_words = num_words(domain_size); - BitSet { - domain_size, - words: vec![0; num_words], - marker: PhantomData, - } - } - - /// Creates a new, filled bitset with a given `domain_size`. - #[inline] - pub fn new_filled(domain_size: usize) -> BitSet<T> { - let num_words = num_words(domain_size); - let mut result = BitSet { - domain_size, - words: vec![!0; num_words], - marker: PhantomData, - }; - result.clear_excess_bits(); - result - } - - /// Gets the domain size. - pub fn domain_size(&self) -> usize { - self.domain_size - } - - /// Clear all elements. - #[inline] - pub fn clear(&mut self) { - for word in &mut self.words { - *word = 0; - } - } - - /// Clear excess bits in the final word. - fn clear_excess_bits(&mut self) { - let num_bits_in_final_word = self.domain_size % WORD_BITS; - if num_bits_in_final_word > 0 { - let mask = (1 << num_bits_in_final_word) - 1; - let final_word_idx = self.words.len() - 1; - self.words[final_word_idx] &= mask; - } - } - - /// Efficiently overwrite `self` with `other`. - pub fn overwrite(&mut self, other: &BitSet<T>) { - assert!(self.domain_size == other.domain_size); - self.words.clone_from_slice(&other.words); - } - - /// Count the number of set bits in the set. - pub fn count(&self) -> usize { - self.words.iter().map(|e| e.count_ones() as usize).sum() - } - - /// Returns `true` if `self` contains `elem`. - #[inline] - pub fn contains(&self, elem: T) -> bool { - assert!(elem.index() < self.domain_size); - let (word_index, mask) = word_index_and_mask(elem); - (self.words[word_index] & mask) != 0 - } - - /// Is `self` is a (non-strict) superset of `other`? - #[inline] - pub fn superset(&self, other: &BitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size); - self.words.iter().zip(&other.words).all(|(a, b)| (a & b) == *b) - } - - /// Is the set empty? - #[inline] - pub fn is_empty(&self) -> bool { - self.words.iter().all(|a| *a == 0) - } - - /// Insert `elem`. Returns whether the set has changed. - #[inline] - pub fn insert(&mut self, elem: T) -> bool { - assert!(elem.index() < self.domain_size); - let (word_index, mask) = word_index_and_mask(elem); - let word_ref = &mut self.words[word_index]; - let word = *word_ref; - let new_word = word | mask; - *word_ref = new_word; - new_word != word - } - - /// Sets all bits to true. - pub fn insert_all(&mut self) { - for word in &mut self.words { - *word = !0; - } - self.clear_excess_bits(); - } - - /// Returns `true` if the set has changed. - #[inline] - pub fn remove(&mut self, elem: T) -> bool { - assert!(elem.index() < self.domain_size); - let (word_index, mask) = word_index_and_mask(elem); - let word_ref = &mut self.words[word_index]; - let word = *word_ref; - let new_word = word & !mask; - *word_ref = new_word; - new_word != word - } - - /// Sets `self = self | other` and returns `true` if `self` changed - /// (i.e., if new bits were added). - pub fn union(&mut self, other: &impl UnionIntoBitSet<T>) -> bool { - other.union_into(self) - } - - /// Sets `self = self - other` and returns `true` if `self` changed. - /// (i.e., if any bits were removed). - pub fn subtract(&mut self, other: &impl SubtractFromBitSet<T>) -> bool { - other.subtract_from(self) - } - - /// Sets `self = self & other` and return `true` if `self` changed. - /// (i.e., if any bits were removed). - pub fn intersect(&mut self, other: &BitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut self.words, &other.words, |a, b| { a & b }) - } - - /// Gets a slice of the underlying words. - pub fn words(&self) -> &[Word] { - &self.words - } - - /// Iterates over the indices of set bits in a sorted order. - #[inline] - pub fn iter(&self) -> BitIter<'_, T> { - BitIter { - cur: None, - iter: self.words.iter().enumerate(), - marker: PhantomData, - } - } - - /// Duplicates the set as a hybrid set. - pub fn to_hybrid(&self) -> HybridBitSet<T> { - // Note: we currently don't bother trying to make a Sparse set. - HybridBitSet::Dense(self.to_owned()) - } - - /// Set `self = self | other`. In contrast to `union` returns `true` if the set contains at - /// least one bit that is not in `other` (i.e. `other` is not a superset of `self`). - /// - /// This is an optimization for union of a hybrid bitset. - fn reverse_union_sparse(&mut self, sparse: &SparseBitSet<T>) -> bool { - assert!(sparse.domain_size == self.domain_size); - self.clear_excess_bits(); - - let mut not_already = false; - // Index of the current word not yet merged. - let mut current_index = 0; - // Mask of bits that came from the sparse set in the current word. - let mut new_bit_mask = 0; - for (word_index, mask) in sparse.iter().map(|x| word_index_and_mask(*x)) { - // Next bit is in a word not inspected yet. - if word_index > current_index { - self.words[current_index] |= new_bit_mask; - // Were there any bits in the old word that did not occur in the sparse set? - not_already |= (self.words[current_index] ^ new_bit_mask) != 0; - // Check all words we skipped for any set bit. - not_already |= self.words[current_index+1..word_index].iter().any(|&x| x != 0); - // Update next word. - current_index = word_index; - // Reset bit mask, no bits have been merged yet. - new_bit_mask = 0; - } - // Add bit and mark it as coming from the sparse set. - // self.words[word_index] |= mask; - new_bit_mask |= mask; - } - self.words[current_index] |= new_bit_mask; - // Any bits in the last inspected word that were not in the sparse set? - not_already |= (self.words[current_index] ^ new_bit_mask) != 0; - // Any bits in the tail? Note `clear_excess_bits` before. - not_already |= self.words[current_index+1..].iter().any(|&x| x != 0); - - not_already - } -} - -/// This is implemented by all the bitsets so that BitSet::union() can be -/// passed any type of bitset. -pub trait UnionIntoBitSet<T: Idx> { - // Performs `other = other | self`. - fn union_into(&self, other: &mut BitSet<T>) -> bool; -} - -/// This is implemented by all the bitsets so that BitSet::subtract() can be -/// passed any type of bitset. -pub trait SubtractFromBitSet<T: Idx> { - // Performs `other = other - self`. - fn subtract_from(&self, other: &mut BitSet<T>) -> bool; -} - -impl<T: Idx> UnionIntoBitSet<T> for BitSet<T> { - fn union_into(&self, other: &mut BitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut other.words, &self.words, |a, b| { a | b }) - } -} - -impl<T: Idx> SubtractFromBitSet<T> for BitSet<T> { - fn subtract_from(&self, other: &mut BitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut other.words, &self.words, |a, b| { a & !b }) - } -} - -impl<T: Idx> fmt::Debug for BitSet<T> { - fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { - w.debug_list() - .entries(self.iter()) - .finish() - } -} - -impl<T: Idx> ToString for BitSet<T> { - fn to_string(&self) -> String { - let mut result = String::new(); - let mut sep = '['; - - // Note: this is a little endian printout of bytes. - - // i tracks how many bits we have printed so far. - let mut i = 0; - for word in &self.words { - let mut word = *word; - for _ in 0..WORD_BYTES { // for each byte in `word`: - let remain = self.domain_size - i; - // If less than a byte remains, then mask just that many bits. - let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF }; - assert!(mask <= 0xFF); - let byte = word & mask; - - result.push_str(&format!("{}{:02x}", sep, byte)); - - if remain <= 8 { break; } - word >>= 8; - i += 8; - sep = '-'; - } - sep = '|'; - } - result.push(']'); - - result - } -} - -pub struct BitIter<'a, T: Idx> { - cur: Option<(Word, usize)>, - iter: iter::Enumerate<slice::Iter<'a, Word>>, - marker: PhantomData<T> -} - -impl<'a, T: Idx> Iterator for BitIter<'a, T> { - type Item = T; - fn next(&mut self) -> Option<T> { - loop { - if let Some((ref mut word, offset)) = self.cur { - let bit_pos = word.trailing_zeros() as usize; - if bit_pos != WORD_BITS { - let bit = 1 << bit_pos; - *word ^= bit; - return Some(T::new(bit_pos + offset)) - } - } - - let (i, word) = self.iter.next()?; - self.cur = Some((*word, WORD_BITS * i)); - } - } -} - -#[inline] -fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool - where Op: Fn(Word, Word) -> Word -{ - assert_eq!(out_vec.len(), in_vec.len()); - let mut changed = false; - for (out_elem, in_elem) in out_vec.iter_mut().zip(in_vec.iter()) { - let old_val = *out_elem; - let new_val = op(old_val, *in_elem); - *out_elem = new_val; - changed |= old_val != new_val; - } - changed -} - -const SPARSE_MAX: usize = 8; - -/// A fixed-size bitset type with a sparse representation and a maximum of -/// `SPARSE_MAX` elements. The elements are stored as a sorted `SmallVec` with -/// no duplicates; although `SmallVec` can spill its elements to the heap, that -/// never happens within this type because of the `SPARSE_MAX` limit. -/// -/// This type is used by `HybridBitSet`; do not use directly. -#[derive(Clone, Debug)] -pub struct SparseBitSet<T: Idx> { - domain_size: usize, - elems: SmallVec<[T; SPARSE_MAX]>, -} - -impl<T: Idx> SparseBitSet<T> { - fn new_empty(domain_size: usize) -> Self { - SparseBitSet { - domain_size, - elems: SmallVec::new() - } - } - - fn len(&self) -> usize { - self.elems.len() - } - - fn is_empty(&self) -> bool { - self.elems.len() == 0 - } - - fn contains(&self, elem: T) -> bool { - assert!(elem.index() < self.domain_size); - self.elems.contains(&elem) - } - - fn insert(&mut self, elem: T) -> bool { - assert!(elem.index() < self.domain_size); - let changed = if let Some(i) = self.elems.iter().position(|&e| e >= elem) { - if self.elems[i] == elem { - // `elem` is already in the set. - false - } else { - // `elem` is smaller than one or more existing elements. - self.elems.insert(i, elem); - true - } - } else { - // `elem` is larger than all existing elements. - self.elems.push(elem); - true - }; - assert!(self.len() <= SPARSE_MAX); - changed - } - - fn remove(&mut self, elem: T) -> bool { - assert!(elem.index() < self.domain_size); - if let Some(i) = self.elems.iter().position(|&e| e == elem) { - self.elems.remove(i); - true - } else { - false - } - } - - fn to_dense(&self) -> BitSet<T> { - let mut dense = BitSet::new_empty(self.domain_size); - for elem in self.elems.iter() { - dense.insert(*elem); - } - dense - } - - fn iter(&self) -> slice::Iter<'_, T> { - self.elems.iter() - } -} - -impl<T: Idx> UnionIntoBitSet<T> for SparseBitSet<T> { - fn union_into(&self, other: &mut BitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size); - let mut changed = false; - for elem in self.iter() { - changed |= other.insert(*elem); - } - changed - } -} - -impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> { - fn subtract_from(&self, other: &mut BitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size); - let mut changed = false; - for elem in self.iter() { - changed |= other.remove(*elem); - } - changed - } -} - -/// A fixed-size bitset type with a hybrid representation: sparse when there -/// are up to a `SPARSE_MAX` elements in the set, but dense when there are more -/// than `SPARSE_MAX`. -/// -/// This type is especially efficient for sets that typically have a small -/// number of elements, but a large `domain_size`, and are cleared frequently. -/// -/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also -/// just be `usize`. -/// -/// All operations that involve an element will panic if the element is equal -/// to or greater than the domain size. All operations that involve two bitsets -/// will panic if the bitsets have differing domain sizes. -#[derive(Clone, Debug)] -pub enum HybridBitSet<T: Idx> { - Sparse(SparseBitSet<T>), - Dense(BitSet<T>), -} - -impl<T: Idx> HybridBitSet<T> { - pub fn new_empty(domain_size: usize) -> Self { - HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size)) - } - - fn domain_size(&self) -> usize { - match self { - HybridBitSet::Sparse(sparse) => sparse.domain_size, - HybridBitSet::Dense(dense) => dense.domain_size, - } - } - - pub fn clear(&mut self) { - let domain_size = self.domain_size(); - *self = HybridBitSet::new_empty(domain_size); - } - - pub fn contains(&self, elem: T) -> bool { - match self { - HybridBitSet::Sparse(sparse) => sparse.contains(elem), - HybridBitSet::Dense(dense) => dense.contains(elem), - } - } - - pub fn superset(&self, other: &HybridBitSet<T>) -> bool { - match (self, other) { - (HybridBitSet::Dense(self_dense), HybridBitSet::Dense(other_dense)) => { - self_dense.superset(other_dense) - } - _ => { - assert!(self.domain_size() == other.domain_size()); - other.iter().all(|elem| self.contains(elem)) - } - } - } - - pub fn is_empty(&self) -> bool { - match self { - HybridBitSet::Sparse(sparse) => sparse.is_empty(), - HybridBitSet::Dense(dense) => dense.is_empty(), - } - } - - pub fn insert(&mut self, elem: T) -> bool { - // No need to check `elem` against `self.domain_size` here because all - // the match cases check it, one way or another. - match self { - HybridBitSet::Sparse(sparse) if sparse.len() < SPARSE_MAX => { - // The set is sparse and has space for `elem`. - sparse.insert(elem) - } - HybridBitSet::Sparse(sparse) if sparse.contains(elem) => { - // The set is sparse and does not have space for `elem`, but - // that doesn't matter because `elem` is already present. - false - } - HybridBitSet::Sparse(sparse) => { - // The set is sparse and full. Convert to a dense set. - let mut dense = sparse.to_dense(); - let changed = dense.insert(elem); - assert!(changed); - *self = HybridBitSet::Dense(dense); - changed - } - HybridBitSet::Dense(dense) => dense.insert(elem), - } - } - - pub fn insert_all(&mut self) { - let domain_size = self.domain_size(); - match self { - HybridBitSet::Sparse(_) => { - *self = HybridBitSet::Dense(BitSet::new_filled(domain_size)); - } - HybridBitSet::Dense(dense) => dense.insert_all(), - } - } - - pub fn remove(&mut self, elem: T) -> bool { - // Note: we currently don't bother going from Dense back to Sparse. - match self { - HybridBitSet::Sparse(sparse) => sparse.remove(elem), - HybridBitSet::Dense(dense) => dense.remove(elem), - } - } - - pub fn union(&mut self, other: &HybridBitSet<T>) -> bool { - match self { - HybridBitSet::Sparse(self_sparse) => { - match other { - HybridBitSet::Sparse(other_sparse) => { - // Both sets are sparse. Add the elements in - // `other_sparse` to `self` one at a time. This - // may or may not cause `self` to be densified. - assert_eq!(self.domain_size(), other.domain_size()); - let mut changed = false; - for elem in other_sparse.iter() { - changed |= self.insert(*elem); - } - changed - } - HybridBitSet::Dense(other_dense) => { - // `self` is sparse and `other` is dense. To - // merge them, we have two available strategies: - // * Densify `self` then merge other - // * Clone other then integrate bits from `self` - // The second strategy requires dedicated method - // since the usual `union` returns the wrong - // result. In the dedicated case the computation - // is slightly faster if the bits of the sparse - // bitset map to only few words of the dense - // representation, i.e. indices are near each - // other. - // - // Benchmarking seems to suggest that the second - // option is worth it. - let mut new_dense = other_dense.clone(); - let changed = new_dense.reverse_union_sparse(self_sparse); - *self = HybridBitSet::Dense(new_dense); - changed - } - } - } - - HybridBitSet::Dense(self_dense) => self_dense.union(other), - } - } - - /// Converts to a dense set, consuming itself in the process. - pub fn to_dense(self) -> BitSet<T> { - match self { - HybridBitSet::Sparse(sparse) => sparse.to_dense(), - HybridBitSet::Dense(dense) => dense, - } - } - - pub fn iter(&self) -> HybridIter<'_, T> { - match self { - HybridBitSet::Sparse(sparse) => HybridIter::Sparse(sparse.iter()), - HybridBitSet::Dense(dense) => HybridIter::Dense(dense.iter()), - } - } -} - -impl<T: Idx> UnionIntoBitSet<T> for HybridBitSet<T> { - fn union_into(&self, other: &mut BitSet<T>) -> bool { - match self { - HybridBitSet::Sparse(sparse) => sparse.union_into(other), - HybridBitSet::Dense(dense) => dense.union_into(other), - } - } -} - -impl<T: Idx> SubtractFromBitSet<T> for HybridBitSet<T> { - fn subtract_from(&self, other: &mut BitSet<T>) -> bool { - match self { - HybridBitSet::Sparse(sparse) => sparse.subtract_from(other), - HybridBitSet::Dense(dense) => dense.subtract_from(other), - } - } -} - -pub enum HybridIter<'a, T: Idx> { - Sparse(slice::Iter<'a, T>), - Dense(BitIter<'a, T>), -} - -impl<'a, T: Idx> Iterator for HybridIter<'a, T> { - type Item = T; - - fn next(&mut self) -> Option<T> { - match self { - HybridIter::Sparse(sparse) => sparse.next().map(|e| *e), - HybridIter::Dense(dense) => dense.next(), - } - } -} - -/// A resizable bitset type with a dense representation. -/// -/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also -/// just be `usize`. -/// -/// All operations that involve an element will panic if the element is equal -/// to or greater than the domain size. -#[derive(Clone, Debug, PartialEq)] -pub struct GrowableBitSet<T: Idx> { - bit_set: BitSet<T>, -} - -impl<T: Idx> GrowableBitSet<T> { - /// Ensure that the set can hold at least `min_domain_size` elements. - pub fn ensure(&mut self, min_domain_size: usize) { - if self.bit_set.domain_size < min_domain_size { - self.bit_set.domain_size = min_domain_size; - } - - let min_num_words = num_words(min_domain_size); - if self.bit_set.words.len() < min_num_words { - self.bit_set.words.resize(min_num_words, 0) - } - } - - pub fn new_empty() -> GrowableBitSet<T> { - GrowableBitSet { bit_set: BitSet::new_empty(0) } - } - - pub fn with_capacity(capacity: usize) -> GrowableBitSet<T> { - GrowableBitSet { bit_set: BitSet::new_empty(capacity) } - } - - /// Returns `true` if the set has changed. - #[inline] - pub fn insert(&mut self, elem: T) -> bool { - self.ensure(elem.index() + 1); - self.bit_set.insert(elem) - } - - #[inline] - pub fn contains(&self, elem: T) -> bool { - let (word_index, mask) = word_index_and_mask(elem); - if let Some(word) = self.bit_set.words.get(word_index) { - (word & mask) != 0 - } else { - false - } - } -} - -/// A fixed-size 2D bit matrix type with a dense representation. -/// -/// `R` and `C` are index types used to identify rows and columns respectively; -/// typically newtyped `usize` wrappers, but they can also just be `usize`. -/// -/// All operations that involve a row and/or column index will panic if the -/// index exceeds the relevant bound. -#[derive(Clone, Debug, Eq, PartialEq, RustcDecodable, RustcEncodable)] -pub struct BitMatrix<R: Idx, C: Idx> { - num_rows: usize, - num_columns: usize, - words: Vec<Word>, - marker: PhantomData<(R, C)>, -} - -impl<R: Idx, C: Idx> BitMatrix<R, C> { - /// Creates a new `rows x columns` matrix, initially empty. - pub fn new(num_rows: usize, num_columns: usize) -> BitMatrix<R, C> { - // For every element, we need one bit for every other - // element. Round up to an even number of words. - let words_per_row = num_words(num_columns); - BitMatrix { - num_rows, - num_columns, - words: vec![0; num_rows * words_per_row], - marker: PhantomData, - } - } - - /// Creates a new matrix, with `row` used as the value for every row. - pub fn from_row_n(row: &BitSet<C>, num_rows: usize) -> BitMatrix<R, C> { - let num_columns = row.domain_size(); - let words_per_row = num_words(num_columns); - assert_eq!(words_per_row, row.words().len()); - BitMatrix { - num_rows, - num_columns, - words: iter::repeat(row.words()).take(num_rows).flatten().cloned().collect(), - marker: PhantomData, - } - } - - pub fn rows(&self) -> impl Iterator<Item = R> { - (0..self.num_rows).map(R::new) - } - - /// The range of bits for a given row. - fn range(&self, row: R) -> (usize, usize) { - let words_per_row = num_words(self.num_columns); - let start = row.index() * words_per_row; - (start, start + words_per_row) - } - - /// Sets the cell at `(row, column)` to true. Put another way, insert - /// `column` to the bitset for `row`. - /// - /// Returns `true` if this changed the matrix. - pub fn insert(&mut self, row: R, column: C) -> bool { - assert!(row.index() < self.num_rows && column.index() < self.num_columns); - let (start, _) = self.range(row); - let (word_index, mask) = word_index_and_mask(column); - let words = &mut self.words[..]; - let word = words[start + word_index]; - let new_word = word | mask; - words[start + word_index] = new_word; - word != new_word - } - - /// Do the bits from `row` contain `column`? Put another way, is - /// the matrix cell at `(row, column)` true? Put yet another way, - /// if the matrix represents (transitive) reachability, can - /// `row` reach `column`? - pub fn contains(&self, row: R, column: C) -> bool { - assert!(row.index() < self.num_rows && column.index() < self.num_columns); - let (start, _) = self.range(row); - let (word_index, mask) = word_index_and_mask(column); - (self.words[start + word_index] & mask) != 0 - } - - /// Returns those indices that are true in rows `a` and `b`. This - /// is an O(n) operation where `n` is the number of elements - /// (somewhat independent from the actual size of the - /// intersection, in particular). - pub fn intersect_rows(&self, row1: R, row2: R) -> Vec<C> { - assert!(row1.index() < self.num_rows && row2.index() < self.num_rows); - let (row1_start, row1_end) = self.range(row1); - let (row2_start, row2_end) = self.range(row2); - let mut result = Vec::with_capacity(self.num_columns); - for (base, (i, j)) in (row1_start..row1_end).zip(row2_start..row2_end).enumerate() { - let mut v = self.words[i] & self.words[j]; - for bit in 0..WORD_BITS { - if v == 0 { - break; - } - if v & 0x1 != 0 { - result.push(C::new(base * WORD_BITS + bit)); - } - v >>= 1; - } - } - result - } - - /// Adds the bits from row `read` to the bits from row `write`, and - /// returns `true` if anything changed. - /// - /// This is used when computing transitive reachability because if - /// you have an edge `write -> read`, because in that case - /// `write` can reach everything that `read` can (and - /// potentially more). - pub fn union_rows(&mut self, read: R, write: R) -> bool { - assert!(read.index() < self.num_rows && write.index() < self.num_rows); - let (read_start, read_end) = self.range(read); - let (write_start, write_end) = self.range(write); - let words = &mut self.words[..]; - let mut changed = false; - for (read_index, write_index) in (read_start..read_end).zip(write_start..write_end) { - let word = words[write_index]; - let new_word = word | words[read_index]; - words[write_index] = new_word; - changed |= word != new_word; - } - changed - } - - /// Adds the bits from `with` to the bits from row `write`, and - /// returns `true` if anything changed. - pub fn union_row_with(&mut self, with: &BitSet<C>, write: R) -> bool { - assert!(write.index() < self.num_rows); - assert_eq!(with.domain_size(), self.num_columns); - let (write_start, write_end) = self.range(write); - let mut changed = false; - for (read_index, write_index) in (0..with.words().len()).zip(write_start..write_end) { - let word = self.words[write_index]; - let new_word = word | with.words()[read_index]; - self.words[write_index] = new_word; - changed |= word != new_word; - } - changed - } - - /// Sets every cell in `row` to true. - pub fn insert_all_into_row(&mut self, row: R) { - assert!(row.index() < self.num_rows); - let (start, end) = self.range(row); - let words = &mut self.words[..]; - for index in start..end { - words[index] = !0; - } - self.clear_excess_bits(row); - } - - /// Clear excess bits in the final word of the row. - fn clear_excess_bits(&mut self, row: R) { - let num_bits_in_final_word = self.num_columns % WORD_BITS; - if num_bits_in_final_word > 0 { - let mask = (1 << num_bits_in_final_word) - 1; - let (_, end) = self.range(row); - let final_word_idx = end - 1; - self.words[final_word_idx] &= mask; - } - } - - /// Gets a slice of the underlying words. - pub fn words(&self) -> &[Word] { - &self.words - } - - /// Iterates through all the columns set to true in a given row of - /// the matrix. - pub fn iter(&self, row: R) -> BitIter<'_, C> { - assert!(row.index() < self.num_rows); - let (start, end) = self.range(row); - BitIter { - cur: None, - iter: self.words[start..end].iter().enumerate(), - marker: PhantomData, - } - } - - /// Returns the number of elements in `row`. - pub fn count(&self, row: R) -> usize { - let (start, end) = self.range(row); - self.words[start..end].iter().map(|e| e.count_ones() as usize).sum() - } -} - -/// A fixed-column-size, variable-row-size 2D bit matrix with a moderately -/// sparse representation. -/// -/// Initially, every row has no explicit representation. If any bit within a -/// row is set, the entire row is instantiated as `Some(<HybridBitSet>)`. -/// Furthermore, any previously uninstantiated rows prior to it will be -/// instantiated as `None`. Those prior rows may themselves become fully -/// instantiated later on if any of their bits are set. -/// -/// `R` and `C` are index types used to identify rows and columns respectively; -/// typically newtyped `usize` wrappers, but they can also just be `usize`. -#[derive(Clone, Debug)] -pub struct SparseBitMatrix<R, C> -where - R: Idx, - C: Idx, -{ - num_columns: usize, - rows: IndexVec<R, Option<HybridBitSet<C>>>, -} - -impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { - /// Creates a new empty sparse bit matrix with no rows or columns. - pub fn new(num_columns: usize) -> Self { - Self { - num_columns, - rows: IndexVec::new(), - } - } - - fn ensure_row(&mut self, row: R) -> &mut HybridBitSet<C> { - // Instantiate any missing rows up to and including row `row` with an - // empty HybridBitSet. - self.rows.ensure_contains_elem(row, || None); - - // Then replace row `row` with a full HybridBitSet if necessary. - let num_columns = self.num_columns; - self.rows[row].get_or_insert_with(|| HybridBitSet::new_empty(num_columns)) - } - - /// Sets the cell at `(row, column)` to true. Put another way, insert - /// `column` to the bitset for `row`. - /// - /// Returns `true` if this changed the matrix. - pub fn insert(&mut self, row: R, column: C) -> bool { - self.ensure_row(row).insert(column) - } - - /// Do the bits from `row` contain `column`? Put another way, is - /// the matrix cell at `(row, column)` true? Put yet another way, - /// if the matrix represents (transitive) reachability, can - /// `row` reach `column`? - pub fn contains(&self, row: R, column: C) -> bool { - self.row(row).map_or(false, |r| r.contains(column)) - } - - /// Adds the bits from row `read` to the bits from row `write`, and - /// returns `true` if anything changed. - /// - /// This is used when computing transitive reachability because if - /// you have an edge `write -> read`, because in that case - /// `write` can reach everything that `read` can (and - /// potentially more). - pub fn union_rows(&mut self, read: R, write: R) -> bool { - if read == write || self.row(read).is_none() { - return false; - } - - self.ensure_row(write); - if let (Some(read_row), Some(write_row)) = self.rows.pick2_mut(read, write) { - write_row.union(read_row) - } else { - unreachable!() - } - } - - /// Union a row, `from`, into the `into` row. - pub fn union_into_row(&mut self, into: R, from: &HybridBitSet<C>) -> bool { - self.ensure_row(into).union(from) - } - - /// Insert all bits in the given row. - pub fn insert_all_into_row(&mut self, row: R) { - self.ensure_row(row).insert_all(); - } - - pub fn rows(&self) -> impl Iterator<Item = R> { - self.rows.indices() - } - - /// Iterates through all the columns set to true in a given row of - /// the matrix. - pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a { - self.row(row).into_iter().flat_map(|r| r.iter()) - } - - pub fn row(&self, row: R) -> Option<&HybridBitSet<C>> { - if let Some(Some(row)) = self.rows.get(row) { - Some(row) - } else { - None - } - } -} - -#[inline] -fn num_words<T: Idx>(domain_size: T) -> usize { - (domain_size.index() + WORD_BITS - 1) / WORD_BITS -} - -#[inline] -fn word_index_and_mask<T: Idx>(elem: T) -> (usize, Word) { - let elem = elem.index(); - let word_index = elem / WORD_BITS; - let mask = 1 << (elem % WORD_BITS); - (word_index, mask) -}
diff --git a/src/librustc_data_structures/fingerprint.rs b/src/librustc_data_structures/fingerprint.rs index c8012bb..b43df60 100644 --- a/src/librustc_data_structures/fingerprint.rs +++ b/src/librustc_data_structures/fingerprint.rs
@@ -76,7 +76,7 @@ impl stable_hasher::StableHasherResult for Fingerprint { #[inline] - fn finish(hasher: stable_hasher::StableHasher<Self>) -> Self { + fn finish(hasher: stable_hasher::StableHasher) -> Self { let (_0, _1) = hasher.finalize(); Fingerprint(_0, _1) }
diff --git a/src/librustc_data_structures/graph/dominators/mod.rs b/src/librustc_data_structures/graph/dominators/mod.rs index 41e6b72..444463c 100644 --- a/src/librustc_data_structures/graph/dominators/mod.rs +++ b/src/librustc_data_structures/graph/dominators/mod.rs
@@ -4,7 +4,7 @@ //! Rice Computer Science TS-06-33870 //! <https://www.cs.rice.edu/~keith/EMBED/dom.pdf> -use super::super::indexed_vec::{Idx, IndexVec}; +use rustc_index::vec::{Idx, IndexVec}; use super::iterate::reverse_post_order; use super::ControlFlowGraph; @@ -17,7 +17,7 @@ dominators_given_rpo(graph, &rpo) } -pub fn dominators_given_rpo<G: ControlFlowGraph>( +fn dominators_given_rpo<G: ControlFlowGraph>( graph: &G, rpo: &[G::Node], ) -> Dominators<G::Node> { @@ -43,14 +43,12 @@ let mut new_idom = None; for pred in graph.predecessors(node) { if immediate_dominators[pred].is_some() { - // (*) // (*) dominators for `pred` have been calculated - new_idom = intersect_opt( - &post_order_rank, - &immediate_dominators, - new_idom, - Some(pred), - ); + new_idom = Some(if let Some(new_idom) = new_idom { + intersect(&post_order_rank, &immediate_dominators, new_idom, pred) + } else { + pred + }); } } @@ -67,19 +65,6 @@ } } -fn intersect_opt<Node: Idx>( - post_order_rank: &IndexVec<Node, usize>, - immediate_dominators: &IndexVec<Node, Option<Node>>, - node1: Option<Node>, - node2: Option<Node>, -) -> Option<Node> { - match (node1, node2) { - (None, None) => None, - (Some(n), None) | (None, Some(n)) => Some(n), - (Some(n1), Some(n2)) => Some(intersect(post_order_rank, immediate_dominators, n1, n2)), - } -} - fn intersect<Node: Idx>( post_order_rank: &IndexVec<Node, usize>, immediate_dominators: &IndexVec<Node, Option<Node>>,
diff --git a/src/librustc_data_structures/graph/implementation/mod.rs b/src/librustc_data_structures/graph/implementation/mod.rs index d269900..9fdcea6 100644 --- a/src/librustc_data_structures/graph/implementation/mod.rs +++ b/src/librustc_data_structures/graph/implementation/mod.rs
@@ -20,7 +20,7 @@ //! the field `next_edge`). Each of those fields is an array that should //! be indexed by the direction (see the type `Direction`). -use crate::bit_set::BitSet; +use rustc_index::bit_set::BitSet; use crate::snapshot_vec::{SnapshotVec, SnapshotVecDelegate}; use std::fmt::Debug; use std::usize; @@ -60,10 +60,10 @@ fn reverse(_: &mut Vec<Edge<N>>, _: ()) {} } -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Debug)] pub struct NodeIndex(pub usize); -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Debug)] pub struct EdgeIndex(pub usize); pub const INVALID_EDGE_INDEX: EdgeIndex = EdgeIndex(usize::MAX); @@ -303,11 +303,11 @@ impl<'g, N: Debug, E: Debug> AdjacentEdges<'g, N, E> { fn targets(self) -> impl Iterator<Item = NodeIndex> + 'g { - self.into_iter().map(|(_, edge)| edge.target) + self.map(|(_, edge)| edge.target) } fn sources(self) -> impl Iterator<Item = NodeIndex> + 'g { - self.into_iter().map(|(_, edge)| edge.source) + self.map(|(_, edge)| edge.source) } }
diff --git a/src/librustc_data_structures/graph/iterate/mod.rs b/src/librustc_data_structures/graph/iterate/mod.rs index c4185fc..e268b28 100644 --- a/src/librustc_data_structures/graph/iterate/mod.rs +++ b/src/librustc_data_structures/graph/iterate/mod.rs
@@ -1,6 +1,6 @@ -use super::super::indexed_vec::IndexVec; -use super::{DirectedGraph, WithNumNodes, WithSuccessors}; -use crate::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use super::{DirectedGraph, WithNumNodes, WithSuccessors, WithStartNode}; +use rustc_index::bit_set::BitSet; #[cfg(test)] mod tests; @@ -85,3 +85,205 @@ Some(n) } } + +/// Allows searches to terminate early with a value. +#[derive(Clone, Copy, Debug)] +pub enum ControlFlow<T> { + Break(T), + Continue, +} + +/// The status of a node in the depth-first search. +/// +/// See the documentation of `TriColorDepthFirstSearch` to see how a node's status is updated +/// during DFS. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum NodeStatus { + /// This node has been examined by the depth-first search but is not yet `Settled`. + /// + /// Also referred to as "gray" or "discovered" nodes in [CLR][]. + /// + /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms + Visited, + + /// This node and all nodes reachable from it have been examined by the depth-first search. + /// + /// Also referred to as "black" or "finished" nodes in [CLR][]. + /// + /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms + Settled, +} + +struct Event<N> { + node: N, + becomes: NodeStatus, +} + +/// A depth-first search that also tracks when all successors of a node have been examined. +/// +/// This is based on the DFS described in [Introduction to Algorithms (1st ed.)][CLR], hereby +/// referred to as **CLR**. However, we use the terminology in [`NodeStatus`][] above instead of +/// "discovered"/"finished" or "white"/"grey"/"black". Each node begins the search with no status, +/// becomes `Visited` when it is first examined by the DFS and is `Settled` when all nodes +/// reachable from it have been examined. This allows us to differentiate between "tree", "back" +/// and "forward" edges (see [`TriColorVisitor::node_examined`]). +/// +/// Unlike the pseudocode in [CLR][], this implementation is iterative and does not use timestamps. +/// We accomplish this by storing `Event`s on the stack that result in a (possible) state change +/// for each node. A `Visited` event signifies that we should examine this node if it has not yet +/// been `Visited` or `Settled`. When a node is examined for the first time, we mark it as +/// `Visited` and push a `Settled` event for it on stack followed by `Visited` events for all of +/// its predecessors, scheduling them for examination. Multiple `Visited` events for a single node +/// may exist on the stack simultaneously if a node has multiple predecessors, but only one +/// `Settled` event will ever be created for each node. After all `Visited` events for a node's +/// successors have been popped off the stack (as well as any new events triggered by visiting +/// those successors), we will pop off that node's `Settled` event. +/// +/// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms +/// [`NodeStatus`]: ./enum.NodeStatus.html +/// [`TriColorVisitor::node_examined`]: ./trait.TriColorVisitor.html#method.node_examined +pub struct TriColorDepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + graph: &'graph G, + stack: Vec<Event<G::Node>>, + visited: BitSet<G::Node>, + settled: BitSet<G::Node>, +} + +impl<G> TriColorDepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + pub fn new(graph: &'graph G) -> Self { + TriColorDepthFirstSearch { + graph, + stack: vec![], + visited: BitSet::new_empty(graph.num_nodes()), + settled: BitSet::new_empty(graph.num_nodes()), + } + } + + /// Performs a depth-first search, starting from the given `root`. + /// + /// This won't visit nodes that are not reachable from `root`. + pub fn run_from<V>(mut self, root: G::Node, visitor: &mut V) -> Option<V::BreakVal> + where + V: TriColorVisitor<G>, + { + use NodeStatus::{Visited, Settled}; + + self.stack.push(Event { node: root, becomes: Visited }); + + loop { + match self.stack.pop()? { + Event { node, becomes: Settled } => { + let not_previously_settled = self.settled.insert(node); + assert!(not_previously_settled, "A node should be settled exactly once"); + if let ControlFlow::Break(val) = visitor.node_settled(node) { + return Some(val); + } + } + + Event { node, becomes: Visited } => { + let not_previously_visited = self.visited.insert(node); + let prior_status = if not_previously_visited { + None + } else if self.settled.contains(node) { + Some(Settled) + } else { + Some(Visited) + }; + + if let ControlFlow::Break(val) = visitor.node_examined(node, prior_status) { + return Some(val); + } + + // If this node has already been examined, we are done. + if prior_status.is_some() { + continue; + } + + // Otherwise, push a `Settled` event for this node onto the stack, then + // schedule its successors for examination. + self.stack.push(Event { node, becomes: Settled }); + for succ in self.graph.successors(node) { + self.stack.push(Event { node: succ, becomes: Visited }); + } + } + } + } + } +} + +impl<G> TriColorDepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors + WithStartNode, +{ + /// Performs a depth-first search, starting from `G::start_node()`. + /// + /// This won't visit nodes that are not reachable from the start node. + pub fn run_from_start<V>(self, visitor: &mut V) -> Option<V::BreakVal> + where + V: TriColorVisitor<G>, + { + let root = self.graph.start_node(); + self.run_from(root, visitor) + } +} + +/// What to do when a node is examined or becomes `Settled` during DFS. +pub trait TriColorVisitor<G> +where + G: ?Sized + DirectedGraph, +{ + /// The value returned by this search. + type BreakVal; + + /// Called when a node is examined by the depth-first search. + /// + /// By checking the value of `prior_status`, this visitor can determine whether the edge + /// leading to this node was a tree edge (`None`), forward edge (`Some(Settled)`) or back edge + /// (`Some(Visited)`). For a full explanation of each edge type, see the "Depth-first Search" + /// chapter in [CLR][] or [wikipedia][]. + /// + /// If you want to know *both* nodes linked by each edge, you'll need to modify + /// `TriColorDepthFirstSearch` to store a `source` node for each `Visited` event. + /// + /// [wikipedia]: https://en.wikipedia.org/wiki/Depth-first_search#Output_of_a_depth-first_search + /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms + fn node_examined( + &mut self, + _target: G::Node, + _prior_status: Option<NodeStatus>, + ) -> ControlFlow<Self::BreakVal> { + ControlFlow::Continue + } + + /// Called after all nodes reachable from this one have been examined. + fn node_settled(&mut self, _target: G::Node) -> ControlFlow<Self::BreakVal> { + ControlFlow::Continue + } +} + +/// This `TriColorVisitor` looks for back edges in a graph, which indicate that a cycle exists. +pub struct CycleDetector; + +impl<G> TriColorVisitor<G> for CycleDetector +where + G: ?Sized + DirectedGraph, +{ + type BreakVal = (); + + fn node_examined( + &mut self, + _node: G::Node, + prior_status: Option<NodeStatus>, + ) -> ControlFlow<Self::BreakVal> { + match prior_status { + Some(NodeStatus::Visited) => ControlFlow::Break(()), + _ => ControlFlow::Continue, + } + } +}
diff --git a/src/librustc_data_structures/graph/iterate/tests.rs b/src/librustc_data_structures/graph/iterate/tests.rs index 6c7cfd6..0e038e8 100644 --- a/src/librustc_data_structures/graph/iterate/tests.rs +++ b/src/librustc_data_structures/graph/iterate/tests.rs
@@ -9,3 +9,14 @@ let result = post_order_from(&graph, 0); assert_eq!(result, vec![3, 1, 2, 0]); } + +#[test] +fn is_cyclic() { + use super::super::is_cyclic; + + let diamond_acyclic = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]); + let diamond_cyclic = TestGraph::new(0, &[(0, 1), (1, 2), (2, 3), (3, 0)]); + + assert!(!is_cyclic(&diamond_acyclic)); + assert!(is_cyclic(&diamond_cyclic)); +}
diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs index 662581c..3733579 100644 --- a/src/librustc_data_structures/graph/mod.rs +++ b/src/librustc_data_structures/graph/mod.rs
@@ -1,4 +1,4 @@ -use super::indexed_vec::Idx; +use rustc_index::vec::Idx; pub mod dominators; pub mod implementation; @@ -81,3 +81,13 @@ + WithNumNodes, { } + +/// Returns `true` if the graph has a cycle that is reachable from the start node. +pub fn is_cyclic<G>(graph: &G) -> bool +where + G: ?Sized + DirectedGraph + WithStartNode + WithSuccessors + WithNumNodes, +{ + iterate::TriColorDepthFirstSearch::new(graph) + .run_from_start(&mut iterate::CycleDetector) + .is_some() +}
diff --git a/src/librustc_data_structures/graph/scc/mod.rs b/src/librustc_data_structures/graph/scc/mod.rs index 23a1a2a..c214f66 100644 --- a/src/librustc_data_structures/graph/scc/mod.rs +++ b/src/librustc_data_structures/graph/scc/mod.rs
@@ -6,7 +6,7 @@ use crate::fx::FxHashSet; use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors}; use crate::graph::vec_graph::VecGraph; -use crate::indexed_vec::{Idx, IndexVec}; +use rustc_index::vec::{Idx, IndexVec}; use std::ops::Range; #[cfg(test)]
diff --git a/src/librustc_data_structures/graph/vec_graph/mod.rs b/src/librustc_data_structures/graph/vec_graph/mod.rs index 19c61f2..aad5944 100644 --- a/src/librustc_data_structures/graph/vec_graph/mod.rs +++ b/src/librustc_data_structures/graph/vec_graph/mod.rs
@@ -1,4 +1,4 @@ -use crate::indexed_vec::{Idx, IndexVec}; +use rustc_index::vec::{Idx, IndexVec}; use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors}; #[cfg(test)]
diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index f759350..474a426 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs
@@ -68,13 +68,12 @@ pub mod svh; pub mod base_n; pub mod binary_search_util; -pub mod bit_set; pub mod box_region; pub mod const_cstr; pub mod flock; pub mod fx; +pub mod stable_map; pub mod graph; -pub mod indexed_vec; pub mod jobserver; pub mod obligation_forest; pub mod owning_ref; @@ -84,6 +83,7 @@ pub mod snapshot_map; pub use ena::snapshot_vec; pub mod sorted_map; +pub mod stable_set; #[macro_use] pub mod stable_hasher; pub mod sync; pub mod sharded;
diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs index 98ae1a5..958ab61 100644 --- a/src/librustc_data_structures/obligation_forest/mod.rs +++ b/src/librustc_data_structures/obligation_forest/mod.rs
@@ -138,7 +138,7 @@ /// call to `compress`. /// /// `usize` indices are used here and throughout this module, rather than - /// `newtype_index!` indices, because this code is hot enough that the + /// `rustc_index::newtype_index!` indices, because this code is hot enough that the /// `u32`-to-`usize` conversions that would be required are significant, /// and space considerations are not important. nodes: Vec<Node<O>>, @@ -149,11 +149,10 @@ /// A cache of the nodes in `nodes`, indexed by predicate. Unfortunately, /// its contents are not guaranteed to match those of `nodes`. See the /// comments in `process_obligation` for details. - waiting_cache: FxHashMap<O::Predicate, usize>, + active_cache: FxHashMap<O::Predicate, usize>, - /// A scratch vector reused in various operations, to avoid allocating new - /// vectors. - scratch: RefCell<Vec<usize>>, + /// A vector reused in compress(), to avoid allocating new vectors. + node_rewrites: RefCell<Vec<usize>>, obligation_tree_id_generator: ObligationTreeIdGenerator, @@ -235,10 +234,6 @@ /// This obligation was resolved to an error. Error nodes are /// removed from the vector by the compression step. Error, - - /// This is a temporary state used in DFS loops to detect cycles, - /// it should not exist outside of these DFSes. - OnDfsStack, } #[derive(Debug)] @@ -278,8 +273,8 @@ ObligationForest { nodes: vec![], done_cache: Default::default(), - waiting_cache: Default::default(), - scratch: RefCell::new(vec![]), + active_cache: Default::default(), + node_rewrites: RefCell::new(vec![]), obligation_tree_id_generator: (0..).map(ObligationTreeId), error_cache: Default::default(), } @@ -303,15 +298,16 @@ return Ok(()); } - match self.waiting_cache.entry(obligation.as_predicate().clone()) { + match self.active_cache.entry(obligation.as_predicate().clone()) { Entry::Occupied(o) => { + let index = *o.get(); debug!("register_obligation_at({:?}, {:?}) - duplicate of {:?}!", - obligation, parent, o.get()); - let node = &mut self.nodes[*o.get()]; + obligation, parent, index); + let node = &mut self.nodes[index]; if let Some(parent_index) = parent { - // If the node is already in `waiting_cache`, it has - // already had its chance to be marked with a parent. So if - // it's not already present, just dump `parent` into the + // If the node is already in `active_cache`, it has already + // had its chance to be marked with a parent. So if it's + // not already present, just dump `parent` into the // dependents as a non-parent. if !node.dependents.contains(&parent_index) { node.dependents.push(parent_index); @@ -342,7 +338,8 @@ if already_failed { Err(()) } else { - v.insert(self.nodes.len()); + let new_index = self.nodes.len(); + v.insert(new_index); self.nodes.push(Node::new(parent, obligation, obligation_tree_id)); Ok(()) } @@ -352,16 +349,16 @@ /// Converts all remaining obligations to the given error. pub fn to_errors<E: Clone>(&mut self, error: E) -> Vec<Error<O, E>> { - let mut errors = vec![]; - for (index, node) in self.nodes.iter().enumerate() { - if let NodeState::Pending = node.state.get() { - let backtrace = self.error_at(index); - errors.push(Error { + let errors = self.nodes.iter().enumerate() + .filter(|(_index, node)| node.state.get() == NodeState::Pending) + .map(|(index, _node)| { + Error { error: error.clone(), - backtrace, - }); - } - } + backtrace: self.error_at(index), + } + }) + .collect(); + let successful_obligations = self.compress(DoCompleted::Yes); assert!(successful_obligations.unwrap().is_empty()); errors @@ -371,15 +368,14 @@ pub fn map_pending_obligations<P, F>(&self, f: F) -> Vec<P> where F: Fn(&O) -> P { - self.nodes - .iter() - .filter(|n| n.state.get() == NodeState::Pending) - .map(|n| f(&n.obligation)) + self.nodes.iter() + .filter(|node| node.state.get() == NodeState::Pending) + .map(|node| f(&node.obligation)) .collect() } - fn insert_into_error_cache(&mut self, node_index: usize) { - let node = &self.nodes[node_index]; + fn insert_into_error_cache(&mut self, index: usize) { + let node = &self.nodes[index]; self.error_cache .entry(node.obligation_tree_id) .or_default() @@ -406,13 +402,13 @@ // `processor.process_obligation` can modify the predicate within // `node.obligation`, and that predicate is the key used for - // `self.waiting_cache`. This means that `self.waiting_cache` can - // get out of sync with `nodes`. It's not very common, but it does + // `self.active_cache`. This means that `self.active_cache` can get + // out of sync with `nodes`. It's not very common, but it does // happen, and code in `compress` has to allow for it. - let result = match node.state.get() { - NodeState::Pending => processor.process_obligation(&mut node.obligation), - _ => continue - }; + if node.state.get() != NodeState::Pending { + continue; + } + let result = processor.process_obligation(&mut node.obligation); debug!("process_obligations: node {} got result {:?}", index, result); @@ -439,10 +435,9 @@ } ProcessResult::Error(err) => { stalled = false; - let backtrace = self.error_at(index); errors.push(Error { error: err, - backtrace, + backtrace: self.error_at(index), }); } } @@ -478,61 +473,53 @@ fn process_cycles<P>(&self, processor: &mut P) where P: ObligationProcessor<Obligation=O> { - let mut stack = self.scratch.replace(vec![]); - debug_assert!(stack.is_empty()); + let mut stack = vec![]; debug!("process_cycles()"); for (index, node) in self.nodes.iter().enumerate() { - // For rustc-benchmarks/inflate-0.1.0 this state test is extremely - // hot and the state is almost always `Pending` or `Waiting`. It's - // a win to handle the no-op cases immediately to avoid the cost of - // the function call. - match node.state.get() { - NodeState::Waiting | NodeState::Pending | NodeState::Done | NodeState::Error => {}, - _ => self.find_cycles_from_node(&mut stack, processor, index), + // For some benchmarks this state test is extremely + // hot. It's a win to handle the no-op cases immediately to avoid + // the cost of the function call. + if node.state.get() == NodeState::Success { + self.find_cycles_from_node(&mut stack, processor, index); } } debug!("process_cycles: complete"); debug_assert!(stack.is_empty()); - self.scratch.replace(stack); } fn find_cycles_from_node<P>(&self, stack: &mut Vec<usize>, processor: &mut P, index: usize) where P: ObligationProcessor<Obligation=O> { let node = &self.nodes[index]; - match node.state.get() { - NodeState::OnDfsStack => { - let index = stack.iter().rposition(|&n| n == index).unwrap(); - processor.process_backedge(stack[index..].iter().map(GetObligation(&self.nodes)), - PhantomData); - } - NodeState::Success => { - node.state.set(NodeState::OnDfsStack); - stack.push(index); - for &index in node.dependents.iter() { - self.find_cycles_from_node(stack, processor, index); + if node.state.get() == NodeState::Success { + match stack.iter().rposition(|&n| n == index) { + None => { + stack.push(index); + for &index in node.dependents.iter() { + self.find_cycles_from_node(stack, processor, index); + } + stack.pop(); + node.state.set(NodeState::Done); } - stack.pop(); - node.state.set(NodeState::Done); - }, - NodeState::Waiting | NodeState::Pending => { - // This node is still reachable from some pending node. We - // will get to it when they are all processed. + Some(rpos) => { + // Cycle detected. + processor.process_backedge( + stack[rpos..].iter().map(GetObligation(&self.nodes)), + PhantomData + ); + } } - NodeState::Done | NodeState::Error => { - // Already processed that node. - } - }; + } } /// Returns a vector of obligations for `p` and all of its /// ancestors, putting them into the error state in the process. fn error_at(&self, mut index: usize) -> Vec<O> { - let mut error_stack = self.scratch.replace(vec![]); + let mut error_stack: Vec<usize> = vec![]; let mut trace = vec![]; loop { @@ -553,15 +540,12 @@ while let Some(index) = error_stack.pop() { let node = &self.nodes[index]; - match node.state.get() { - NodeState::Error => continue, - _ => node.state.set(NodeState::Error), + if node.state.get() != NodeState::Error { + node.state.set(NodeState::Error); + error_stack.extend(node.dependents.iter()); } - - error_stack.extend(node.dependents.iter()); } - self.scratch.replace(error_stack); trace } @@ -569,7 +553,19 @@ #[inline(always)] fn inlined_mark_neighbors_as_waiting_from(&self, node: &Node<O>) { for &index in node.dependents.iter() { - self.mark_as_waiting_from(&self.nodes[index]); + let node = &self.nodes[index]; + match node.state.get() { + NodeState::Waiting | NodeState::Error => {} + NodeState::Success => { + node.state.set(NodeState::Waiting); + // This call site is cold. + self.uninlined_mark_neighbors_as_waiting_from(node); + } + NodeState::Pending | NodeState::Done => { + // This call site is cold. + self.uninlined_mark_neighbors_as_waiting_from(node); + } + } } } @@ -595,37 +591,28 @@ } } - fn mark_as_waiting_from(&self, node: &Node<O>) { - match node.state.get() { - NodeState::Waiting | NodeState::Error | NodeState::OnDfsStack => return, - NodeState::Success => node.state.set(NodeState::Waiting), - NodeState::Pending | NodeState::Done => {}, - } - - // This call site is cold. - self.uninlined_mark_neighbors_as_waiting_from(node); - } - - /// Compresses the vector, removing all popped nodes. This adjusts - /// the indices and hence invalidates any outstanding - /// indices. Cannot be used during a transaction. + /// Compresses the vector, removing all popped nodes. This adjusts the + /// indices and hence invalidates any outstanding indices. /// /// Beforehand, all nodes must be marked as `Done` and no cycles /// on these nodes may be present. This is done by e.g., `process_cycles`. #[inline(never)] fn compress(&mut self, do_completed: DoCompleted) -> Option<Vec<O>> { - let nodes_len = self.nodes.len(); - let mut node_rewrites: Vec<_> = self.scratch.replace(vec![]); - node_rewrites.extend(0..nodes_len); + let orig_nodes_len = self.nodes.len(); + let mut node_rewrites: Vec<_> = self.node_rewrites.replace(vec![]); + debug_assert!(node_rewrites.is_empty()); + node_rewrites.extend(0..orig_nodes_len); let mut dead_nodes = 0; + let mut removed_done_obligations: Vec<O> = vec![]; - // Now move all popped nodes to the end. Try to keep the order. + // Now move all Done/Error nodes to the end, preserving the order of + // the Pending/Waiting nodes. // // LOOP INVARIANT: // self.nodes[0..index - dead_nodes] are the first remaining nodes // self.nodes[index - dead_nodes..index] are all dead // self.nodes[index..] are unchanged - for index in 0..self.nodes.len() { + for index in 0..orig_nodes_len { let node = &self.nodes[index]; match node.state.get() { NodeState::Pending | NodeState::Waiting => { @@ -636,88 +623,77 @@ } NodeState::Done => { // This lookup can fail because the contents of - // `self.waiting_cache` is not guaranteed to match those of + // `self.active_cache` are not guaranteed to match those of // `self.nodes`. See the comment in `process_obligation` // for more details. - if let Some((predicate, _)) = self.waiting_cache - .remove_entry(node.obligation.as_predicate()) + if let Some((predicate, _)) = + self.active_cache.remove_entry(node.obligation.as_predicate()) { self.done_cache.insert(predicate); } else { self.done_cache.insert(node.obligation.as_predicate().clone()); } - node_rewrites[index] = nodes_len; + if do_completed == DoCompleted::Yes { + // Extract the success stories. + removed_done_obligations.push(node.obligation.clone()); + } + node_rewrites[index] = orig_nodes_len; dead_nodes += 1; } NodeState::Error => { // We *intentionally* remove the node from the cache at this point. Otherwise // tests must come up with a different type on every type error they // check against. - self.waiting_cache.remove(node.obligation.as_predicate()); - node_rewrites[index] = nodes_len; - dead_nodes += 1; + self.active_cache.remove(node.obligation.as_predicate()); self.insert_into_error_cache(index); + node_rewrites[index] = orig_nodes_len; + dead_nodes += 1; } - NodeState::OnDfsStack | NodeState::Success => unreachable!() + NodeState::Success => unreachable!() } } - // No compression needed. - if dead_nodes == 0 { - node_rewrites.truncate(0); - self.scratch.replace(node_rewrites); - return if do_completed == DoCompleted::Yes { Some(vec![]) } else { None }; + if dead_nodes > 0 { + // Remove the dead nodes and rewrite indices. + self.nodes.truncate(orig_nodes_len - dead_nodes); + self.apply_rewrites(&node_rewrites); } - // Pop off all the nodes we killed and extract the success stories. - let successful = if do_completed == DoCompleted::Yes { - Some((0..dead_nodes) - .map(|_| self.nodes.pop().unwrap()) - .flat_map(|node| { - match node.state.get() { - NodeState::Error => None, - NodeState::Done => Some(node.obligation), - _ => unreachable!() - } - }) - .collect()) - } else { - self.nodes.truncate(self.nodes.len() - dead_nodes); - None - }; - self.apply_rewrites(&node_rewrites); - node_rewrites.truncate(0); - self.scratch.replace(node_rewrites); + self.node_rewrites.replace(node_rewrites); - successful + if do_completed == DoCompleted::Yes { + Some(removed_done_obligations) + } else { + None + } } fn apply_rewrites(&mut self, node_rewrites: &[usize]) { - let nodes_len = node_rewrites.len(); + let orig_nodes_len = node_rewrites.len(); for node in &mut self.nodes { - let mut index = 0; - while index < node.dependents.len() { - let new_index = node_rewrites[node.dependents[index]]; - if new_index >= nodes_len { - node.dependents.swap_remove(index); - if index == 0 && node.has_parent { + let mut i = 0; + while i < node.dependents.len() { + let new_index = node_rewrites[node.dependents[i]]; + if new_index >= orig_nodes_len { + node.dependents.swap_remove(i); + if i == 0 && node.has_parent { // We just removed the parent. node.has_parent = false; } } else { - node.dependents[index] = new_index; - index += 1; + node.dependents[i] = new_index; + i += 1; } } } - // This updating of `self.waiting_cache` is necessary because the + // This updating of `self.active_cache` is necessary because the // removal of nodes within `compress` can fail. See above. - self.waiting_cache.retain(|_predicate, index| { + self.active_cache.retain(|_predicate, index| { let new_index = node_rewrites[*index]; - if new_index >= nodes_len { + if new_index >= orig_nodes_len { false } else { *index = new_index;
diff --git a/src/librustc_data_structures/obligation_forest/tests.rs b/src/librustc_data_structures/obligation_forest/tests.rs index e204665..54b6f6d 100644 --- a/src/librustc_data_structures/obligation_forest/tests.rs +++ b/src/librustc_data_structures/obligation_forest/tests.rs
@@ -116,7 +116,9 @@ _ => unreachable!(), } }, |_| {}), DoCompleted::Yes); - assert_eq!(ok.unwrap(), vec!["A.3", "A.1", "A.3.i"]); + let mut ok = ok.unwrap(); + ok.sort(); + assert_eq!(ok, vec!["A.1", "A.3", "A.3.i"]); assert_eq!(err, vec![Error { error: "A is for apple", @@ -132,7 +134,9 @@ _ => panic!("unexpected obligation {:?}", obligation), } }, |_| {}), DoCompleted::Yes); - assert_eq!(ok.unwrap(), vec!["D.2.i", "D.2"]); + let mut ok = ok.unwrap(); + ok.sort(); + assert_eq!(ok, vec!["D.2", "D.2.i"]); assert_eq!(err, vec![Error { error: "D is for dumb", @@ -172,7 +176,9 @@ _ => unreachable!(), } }, |_| {}), DoCompleted::Yes); - assert_eq!(ok.unwrap(), vec!["A.3", "A.1"]); + let mut ok = ok.unwrap(); + ok.sort(); + assert_eq!(ok, vec!["A.1", "A.3"]); assert!(err.is_empty()); let Outcome { completed: ok, errors: err, .. } = @@ -193,7 +199,9 @@ _ => unreachable!(), } }, |_| {}), DoCompleted::Yes); - assert_eq!(ok.unwrap(), vec!["A.2.i.a", "A.2.i", "A.2", "A"]); + let mut ok = ok.unwrap(); + ok.sort(); + assert_eq!(ok, vec!["A", "A.2", "A.2.i", "A.2.i.a"]); assert!(err.is_empty()); let Outcome { completed: ok, errors: err, .. } = @@ -261,7 +269,9 @@ } }, |_|{}), DoCompleted::Yes); assert_eq!(d_count, 1); - assert_eq!(ok.unwrap(), vec!["D", "A.2", "A.1", "A"]); + let mut ok = ok.unwrap(); + ok.sort(); + assert_eq!(ok, vec!["A", "A.1", "A.2", "D"]); assert_eq!(err.len(), 0); let errors = forest.to_errors(()); @@ -323,7 +333,9 @@ _ => unreachable!(), } }, |_|{}), DoCompleted::Yes); - assert_eq!(ok.unwrap(), vec!["C: Sized", "B: Sized", "A: Sized"]); + let mut ok = ok.unwrap(); + ok.sort(); + assert_eq!(ok, vec!["A: Sized", "B: Sized", "C: Sized"]); assert_eq!(err.len(), 0); forest.register_obligation("(A,B,C): Sized"); @@ -361,7 +373,9 @@ _ => unreachable!(), } }, |_|{}), DoCompleted::Yes); - assert_eq!(ok.unwrap(), vec!["C2", "C1"]); + let mut ok = ok.unwrap(); + ok.sort(); + assert_eq!(ok, vec!["C1", "C2"]); assert_eq!(err.len(), 0); let Outcome { completed: ok, errors: err, .. } =
diff --git a/src/librustc_data_structures/owning_ref/mod.rs b/src/librustc_data_structures/owning_ref/mod.rs index b835b17..0213eb4 100644 --- a/src/librustc_data_structures/owning_ref/mod.rs +++ b/src/librustc_data_structures/owning_ref/mod.rs
@@ -1046,14 +1046,14 @@ where O: CloneStableAddress {} unsafe impl<O, T: ?Sized> Send for OwningRef<O, T> - where O: Send, for<'a> (&'a T): Send {} + where O: Send, for<'a> &'a T: Send {} unsafe impl<O, T: ?Sized> Sync for OwningRef<O, T> - where O: Sync, for<'a> (&'a T): Sync {} + where O: Sync, for<'a> &'a T: Sync {} unsafe impl<O, T: ?Sized> Send for OwningRefMut<O, T> - where O: Send, for<'a> (&'a mut T): Send {} + where O: Send, for<'a> &'a mut T: Send {} unsafe impl<O, T: ?Sized> Sync for OwningRefMut<O, T> - where O: Sync, for<'a> (&'a mut T): Sync {} + where O: Sync, for<'a> &'a mut T: Sync {} impl Debug for dyn Erased { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/src/librustc_data_structures/sharded.rs b/src/librustc_data_structures/sharded.rs index 31cb220..2f972ee 100644 --- a/src/librustc_data_structures/sharded.rs +++ b/src/librustc_data_structures/sharded.rs
@@ -2,6 +2,7 @@ use std::mem; use std::borrow::Borrow; use std::collections::hash_map::RawEntryMut; +use smallvec::SmallVec; use crate::fx::{FxHasher, FxHashMap}; use crate::sync::{Lock, LockGuard}; @@ -18,7 +19,7 @@ #[cfg(not(parallel_compiler))] const SHARD_BITS: usize = 0; -const SHARDS: usize = 1 << SHARD_BITS; +pub const SHARDS: usize = 1 << SHARD_BITS; /// An array of cache-line aligned inner locked structures with convenience methods. #[derive(Clone)] @@ -29,21 +30,36 @@ impl<T: Default> Default for Sharded<T> { #[inline] fn default() -> Self { + Self::new(|| T::default()) + } +} + +impl<T> Sharded<T> { + #[inline] + pub fn new(mut value: impl FnMut() -> T) -> Self { + // Create a vector of the values we want + let mut values: SmallVec<[_; SHARDS]> = (0..SHARDS).map(|_| { + CacheAligned(Lock::new(value())) + }).collect(); + + // Create an unintialized array let mut shards: mem::MaybeUninit<[CacheAligned<Lock<T>>; SHARDS]> = mem::MaybeUninit::uninit(); - let first = shards.as_mut_ptr() as *mut CacheAligned<Lock<T>>; + unsafe { - for i in 0..SHARDS { - first.add(i).write(CacheAligned(Lock::new(T::default()))); - } + // Copy the values into our array + let first = shards.as_mut_ptr() as *mut CacheAligned<Lock<T>>; + values.as_ptr().copy_to_nonoverlapping(first, SHARDS); + + // Ignore the content of the vector + values.set_len(0); + Sharded { shards: shards.assume_init(), } } } -} -impl<T> Sharded<T> { #[inline] pub fn get_shard_by_value<K: Hash + ?Sized>(&self, val: &K) -> &Lock<T> { if SHARDS == 1 { @@ -74,7 +90,7 @@ pub type ShardedHashMap<K, V> = Sharded<FxHashMap<K, V>>; -impl<K: Eq + Hash, V> ShardedHashMap<K, V> { +impl<K: Eq, V> ShardedHashMap<K, V> { pub fn len(&self) -> usize { self.lock_shards().iter().map(|shard| shard.len()).sum() }
diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index ce0aa07..bdd3dc9 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs
@@ -7,7 +7,7 @@ mod tests; pub struct SnapshotMap<K, V> - where K: Hash + Clone + Eq + where K: Clone + Eq { map: FxHashMap<K, V>, undo_log: Vec<UndoLog<K, V>>,
diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index 47dfc1d..092208c 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs
@@ -1,10 +1,9 @@ use std::hash::{Hash, Hasher, BuildHasher}; -use std::marker::PhantomData; use std::mem; use smallvec::SmallVec; use crate::sip128::SipHasher128; -use crate::indexed_vec; -use crate::bit_set; +use rustc_index::vec; +use rustc_index::bit_set; /// When hashing something that ends up affecting properties like symbol names, /// we want these symbol names to be calculated independently of other factors @@ -13,55 +12,53 @@ /// To that end we always convert integers to little-endian format before /// hashing and the architecture dependent `isize` and `usize` types are /// extended to 64 bits if needed. -pub struct StableHasher<W> { +pub struct StableHasher { state: SipHasher128, - width: PhantomData<W>, } -impl<W: StableHasherResult> ::std::fmt::Debug for StableHasher<W> { +impl ::std::fmt::Debug for StableHasher { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { write!(f, "{:?}", self.state) } } pub trait StableHasherResult: Sized { - fn finish(hasher: StableHasher<Self>) -> Self; + fn finish(hasher: StableHasher) -> Self; } -impl<W: StableHasherResult> StableHasher<W> { +impl StableHasher { pub fn new() -> Self { StableHasher { state: SipHasher128::new_with_keys(0, 0), - width: PhantomData, } } - pub fn finish(self) -> W { + pub fn finish<W: StableHasherResult>(self) -> W { W::finish(self) } } impl StableHasherResult for u128 { - fn finish(hasher: StableHasher<Self>) -> Self { + fn finish(hasher: StableHasher) -> Self { let (_0, _1) = hasher.finalize(); u128::from(_0) | (u128::from(_1) << 64) } } impl StableHasherResult for u64 { - fn finish(hasher: StableHasher<Self>) -> Self { + fn finish(hasher: StableHasher) -> Self { hasher.finalize().0 } } -impl<W> StableHasher<W> { +impl StableHasher { #[inline] pub fn finalize(self) -> (u64, u64) { self.state.finish128() } } -impl<W> Hasher for StableHasher<W> { +impl Hasher for StableHasher { fn finish(&self) -> u64 { panic!("use StableHasher::finalize instead"); } @@ -165,16 +162,14 @@ /// `StableHasher` takes care of endianness and `isize`/`usize` platform /// differences. pub trait HashStable<CTX> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut CTX, - hasher: &mut StableHasher<W>); + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher); } /// Implement this for types that can be turned into stable keys like, for /// example, for DefId that can be converted to a DefPathHash. This is used for /// bringing maps into a predictable order before hashing them. pub trait ToStableHashKey<HCX> { - type KeyType: Ord + Clone + Sized + HashStable<HCX>; + type KeyType: Ord + Sized + HashStable<HCX>; fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType; } @@ -185,10 +180,10 @@ ($t:ty) => ( impl<CTX> $crate::stable_hasher::HashStable<CTX> for $t { #[inline] - fn hash_stable<W: $crate::stable_hasher::StableHasherResult>( + fn hash_stable( &self, _: &mut CTX, - hasher: &mut $crate::stable_hasher::StableHasher<W> + hasher: &mut $crate::stable_hasher::StableHasher ) { ::std::hash::Hash::hash(self, hasher); } @@ -215,17 +210,13 @@ impl_stable_hash_via_hash!(()); impl<CTX> HashStable<CTX> for ::std::num::NonZeroU32 { - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { self.get().hash_stable(ctx, hasher) } } impl<CTX> HashStable<CTX> for f32 { - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { let val: u32 = unsafe { ::std::mem::transmute(*self) }; @@ -234,9 +225,7 @@ } impl<CTX> HashStable<CTX> for f64 { - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { let val: u64 = unsafe { ::std::mem::transmute(*self) }; @@ -245,26 +234,20 @@ } impl<CTX> HashStable<CTX> for ::std::cmp::Ordering { - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { (*self as i8).hash_stable(ctx, hasher); } } impl<T1: HashStable<CTX>, CTX> HashStable<CTX> for (T1,) { - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { let (ref _0,) = *self; _0.hash_stable(ctx, hasher); } } impl<T1: HashStable<CTX>, T2: HashStable<CTX>, CTX> HashStable<CTX> for (T1, T2) { - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { let (ref _0, ref _1) = *self; _0.hash_stable(ctx, hasher); _1.hash_stable(ctx, hasher); @@ -276,9 +259,7 @@ T2: HashStable<CTX>, T3: HashStable<CTX>, { - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { let (ref _0, ref _1, ref _2) = *self; _0.hash_stable(ctx, hasher); _1.hash_stable(ctx, hasher); @@ -292,9 +273,7 @@ T3: HashStable<CTX>, T4: HashStable<CTX>, { - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { let (ref _0, ref _1, ref _2, ref _3) = *self; _0.hash_stable(ctx, hasher); _1.hash_stable(ctx, hasher); @@ -304,9 +283,7 @@ } impl<T: HashStable<CTX>, CTX> HashStable<CTX> for [T] { - default fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + default fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { self.len().hash_stable(ctx, hasher); for item in self { item.hash_stable(ctx, hasher); @@ -316,9 +293,7 @@ impl<T: HashStable<CTX>, CTX> HashStable<CTX> for Vec<T> { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { (&self[..]).hash_stable(ctx, hasher); } } @@ -329,9 +304,7 @@ R: BuildHasher, { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { self.len().hash_stable(ctx, hasher); for kv in self { kv.hash_stable(ctx, hasher); @@ -344,9 +317,7 @@ R: BuildHasher, { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { self.len().hash_stable(ctx, hasher); for key in self { key.hash_stable(ctx, hasher); @@ -356,45 +327,35 @@ impl<A, CTX> HashStable<CTX> for SmallVec<[A; 1]> where A: HashStable<CTX> { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { (&self[..]).hash_stable(ctx, hasher); } } impl<T: ?Sized + HashStable<CTX>, CTX> HashStable<CTX> for Box<T> { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { (**self).hash_stable(ctx, hasher); } } impl<T: ?Sized + HashStable<CTX>, CTX> HashStable<CTX> for ::std::rc::Rc<T> { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { (**self).hash_stable(ctx, hasher); } } impl<T: ?Sized + HashStable<CTX>, CTX> HashStable<CTX> for ::std::sync::Arc<T> { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { (**self).hash_stable(ctx, hasher); } } impl<CTX> HashStable<CTX> for str { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - _: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) { self.len().hash(hasher); self.as_bytes().hash(hasher); } @@ -403,9 +364,7 @@ impl<CTX> HashStable<CTX> for String { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { (&self[..]).hash_stable(hcx, hasher); } } @@ -420,9 +379,7 @@ impl<CTX> HashStable<CTX> for bool { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { (if *self { 1u8 } else { 0u8 }).hash_stable(ctx, hasher); } } @@ -432,9 +389,7 @@ where T: HashStable<CTX> { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { if let Some(ref value) = *self { 1u8.hash_stable(ctx, hasher); value.hash_stable(ctx, hasher); @@ -449,9 +404,7 @@ T2: HashStable<CTX>, { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(ctx, hasher); match *self { Ok(ref x) => x.hash_stable(ctx, hasher), @@ -464,28 +417,22 @@ where T: HashStable<CTX> + ?Sized { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { (**self).hash_stable(ctx, hasher); } } impl<T, CTX> HashStable<CTX> for ::std::mem::Discriminant<T> { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - _: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) { ::std::hash::Hash::hash(self, hasher); } } -impl<I: indexed_vec::Idx, T, CTX> HashStable<CTX> for indexed_vec::IndexVec<I, T> +impl<I: vec::Idx, T, CTX> HashStable<CTX> for vec::IndexVec<I, T> where T: HashStable<CTX>, { - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { self.len().hash_stable(ctx, hasher); for v in &self.raw { v.hash_stable(ctx, hasher); @@ -494,21 +441,17 @@ } -impl<I: indexed_vec::Idx, CTX> HashStable<CTX> for bit_set::BitSet<I> +impl<I: vec::Idx, CTX> HashStable<CTX> for bit_set::BitSet<I> { - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { self.words().hash_stable(ctx, hasher); } } -impl<R: indexed_vec::Idx, C: indexed_vec::Idx, CTX> HashStable<CTX> +impl<R: vec::Idx, C: vec::Idx, CTX> HashStable<CTX> for bit_set::BitMatrix<R, C> { - fn hash_stable<W: StableHasherResult>(&self, - ctx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { self.words().hash_stable(ctx, hasher); } } @@ -517,25 +460,21 @@ impl_stable_hash_via_hash!(::std::path::PathBuf); impl<K, V, R, HCX> HashStable<HCX> for ::std::collections::HashMap<K, V, R> - where K: ToStableHashKey<HCX> + Eq + Hash, + where K: ToStableHashKey<HCX> + Eq, V: HashStable<HCX>, R: BuildHasher, { #[inline] - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut HCX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { hash_stable_hashmap(hcx, hasher, self, ToStableHashKey::to_stable_hash_key); } } impl<K, R, HCX> HashStable<HCX> for ::std::collections::HashSet<K, R> - where K: ToStableHashKey<HCX> + Eq + Hash, + where K: ToStableHashKey<HCX> + Eq, R: BuildHasher, { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut HCX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { let mut keys: Vec<_> = self.iter() .map(|k| k.to_stable_hash_key(hcx)) .collect(); @@ -548,9 +487,7 @@ where K: ToStableHashKey<HCX>, V: HashStable<HCX>, { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut HCX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { let mut entries: Vec<_> = self.iter() .map(|(k, v)| (k.to_stable_hash_key(hcx), v)) .collect(); @@ -562,9 +499,7 @@ impl<K, HCX> HashStable<HCX> for ::std::collections::BTreeSet<K> where K: ToStableHashKey<HCX>, { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut HCX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { let mut keys: Vec<_> = self.iter() .map(|k| k.to_stable_hash_key(hcx)) .collect(); @@ -573,17 +508,16 @@ } } -pub fn hash_stable_hashmap<HCX, K, V, R, SK, F, W>( +pub fn hash_stable_hashmap<HCX, K, V, R, SK, F>( hcx: &mut HCX, - hasher: &mut StableHasher<W>, + hasher: &mut StableHasher, map: &::std::collections::HashMap<K, V, R>, to_stable_hash_key: F) - where K: Eq + Hash, + where K: Eq, V: HashStable<HCX>, R: BuildHasher, - SK: HashStable<HCX> + Ord + Clone, + SK: HashStable<HCX> + Ord, F: Fn(&K, &HCX) -> SK, - W: StableHasherResult, { let mut entries: Vec<_> = map.iter() .map(|(k, v)| (to_stable_hash_key(k, hcx), v)) @@ -614,9 +548,7 @@ impl<T, HCX> HashStable<HCX> for StableVec<T> where T: HashStable<HCX> + ToStableHashKey<HCX> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut HCX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { let StableVec(ref v) = *self; let mut sorted: Vec<_> = v.iter()
diff --git a/src/librustc_data_structures/stable_map.rs b/src/librustc_data_structures/stable_map.rs new file mode 100644 index 0000000..f69f28e --- /dev/null +++ b/src/librustc_data_structures/stable_map.rs
@@ -0,0 +1,99 @@ +pub use rustc_hash::FxHashMap; +use std::borrow::Borrow; +use std::collections::hash_map::Entry; +use std::fmt; +use std::hash::Hash; + +/// A deterministic wrapper around FxHashMap that does not provide iteration support. +/// +/// It supports insert, remove, get and get_mut functions from FxHashMap. +/// It also allows to convert hashmap to a sorted vector with the method `into_sorted_vector()`. +#[derive(Clone)] +pub struct StableMap<K, V> { + base: FxHashMap<K, V>, +} + +impl<K, V> Default for StableMap<K, V> +where + K: Eq + Hash, +{ + fn default() -> StableMap<K, V> { + StableMap::new() + } +} + +impl<K, V> fmt::Debug for StableMap<K, V> +where + K: Eq + Hash + fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.base) + } +} + +impl<K, V> PartialEq for StableMap<K, V> +where + K: Eq + Hash, + V: PartialEq, +{ + fn eq(&self, other: &StableMap<K, V>) -> bool { + self.base == other.base + } +} + +impl<K, V> Eq for StableMap<K, V> +where + K: Eq + Hash, + V: Eq, +{} + +impl<K, V> StableMap<K, V> +where + K: Eq + Hash, +{ + pub fn new() -> StableMap<K, V> { + StableMap { base: FxHashMap::default() } + } + + pub fn into_sorted_vector(self) -> Vec<(K, V)> + where + K: Ord + Copy, + { + let mut vector = self.base.into_iter().collect::<Vec<_>>(); + vector.sort_unstable_by_key(|pair| pair.0); + vector + } + + pub fn entry(&mut self, k: K) -> Entry<'_, K, V> { + self.base.entry(k) + } + + pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> + where + K: Borrow<Q>, + Q: Hash + Eq, + { + self.base.get(k) + } + + pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V> + where + K: Borrow<Q>, + Q: Hash + Eq, + { + self.base.get_mut(k) + } + + pub fn insert(&mut self, k: K, v: V) -> Option<V> { + self.base.insert(k, v) + } + + pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V> + where + K: Borrow<Q>, + Q: Hash + Eq, + { + self.base.remove(k) + } +}
diff --git a/src/librustc_data_structures/stable_set.rs b/src/librustc_data_structures/stable_set.rs new file mode 100644 index 0000000..c7ca74f --- /dev/null +++ b/src/librustc_data_structures/stable_set.rs
@@ -0,0 +1,77 @@ +pub use rustc_hash::FxHashSet; +use std::borrow::Borrow; +use std::fmt; +use std::hash::Hash; + +/// A deterministic wrapper around FxHashSet that does not provide iteration support. +/// +/// It supports insert, remove, get functions from FxHashSet. +/// It also allows to convert hashset to a sorted vector with the method `into_sorted_vector()`. +#[derive(Clone)] +pub struct StableSet<T> { + base: FxHashSet<T>, +} + +impl<T> Default for StableSet<T> +where + T: Eq + Hash, +{ + fn default() -> StableSet<T> { + StableSet::new() + } +} + +impl<T> fmt::Debug for StableSet<T> +where + T: Eq + Hash + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.base) + } +} + +impl<T> PartialEq<StableSet<T>> for StableSet<T> +where + T: Eq + Hash, +{ + fn eq(&self, other: &StableSet<T>) -> bool { + self.base == other.base + } +} + +impl<T> Eq for StableSet<T> where T: Eq + Hash {} + +impl<T: Hash + Eq> StableSet<T> { + pub fn new() -> StableSet<T> { + StableSet { base: FxHashSet::default() } + } + + pub fn into_sorted_vector(self) -> Vec<T> + where + T: Ord, + { + let mut vector = self.base.into_iter().collect::<Vec<_>>(); + vector.sort_unstable(); + vector + } + + pub fn get<Q: ?Sized>(&self, value: &Q) -> Option<&T> + where + T: Borrow<Q>, + Q: Hash + Eq, + { + self.base.get(value) + } + + pub fn insert(&mut self, value: T) -> bool { + self.base.insert(value) + } + + pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool + where + T: Borrow<Q>, + Q: Hash + Eq, + { + self.base.remove(value) + } +}
diff --git a/src/librustc_data_structures/svh.rs b/src/librustc_data_structures/svh.rs index 3123c18..6404226 100644 --- a/src/librustc_data_structures/svh.rs +++ b/src/librustc_data_structures/svh.rs
@@ -61,11 +61,7 @@ impl<T> stable_hasher::HashStable<T> for Svh { #[inline] - fn hash_stable<W: stable_hasher::StableHasherResult>( - &self, - ctx: &mut T, - hasher: &mut stable_hasher::StableHasher<W> - ) { + fn hash_stable(&self, ctx: &mut T, hasher: &mut stable_hasher::StableHasher) { let Svh { hash } = *self;
diff --git a/src/librustc_data_structures/sync.rs b/src/librustc_data_structures/sync.rs index 3277b85..6a19f52 100644 --- a/src/librustc_data_structures/sync.rs +++ b/src/librustc_data_structures/sync.rs
@@ -1,6 +1,6 @@ //! This module defines types which are thread safe if cfg!(parallel_compiler) is true. //! -//! `Lrc` is an alias of either Rc or Arc. +//! `Lrc` is an alias of `Arc` if cfg!(parallel_compiler) is true, `Rc` otherwise. //! //! `Lock` is a mutex. //! It internally uses `parking_lot::Mutex` if cfg!(parallel_compiler) is true, @@ -12,7 +12,7 @@ //! //! `MTLock` is a mutex which disappears if cfg!(parallel_compiler) is false. //! -//! `MTRef` is a immutable reference if cfg!(parallel_compiler), and an mutable reference otherwise. +//! `MTRef` is an immutable reference if cfg!(parallel_compiler), and a mutable reference otherwise. //! //! `rustc_erase_owner!` erases a OwningRef owner into Erased or Erased + Send + Sync //! depending on the value of cfg!(parallel_compiler). @@ -23,29 +23,6 @@ use std::ops::{Deref, DerefMut}; use crate::owning_ref::{Erased, OwningRef}; -pub fn serial_join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB) - where A: FnOnce() -> RA, - B: FnOnce() -> RB -{ - (oper_a(), oper_b()) -} - -pub struct SerialScope; - -impl SerialScope { - pub fn spawn<F>(&self, f: F) - where F: FnOnce(&SerialScope) - { - f(self) - } -} - -pub fn serial_scope<F, R>(f: F) -> R - where F: FnOnce(&SerialScope) -> R -{ - f(&SerialScope) -} - pub use std::sync::atomic::Ordering::SeqCst; pub use std::sync::atomic::Ordering; @@ -176,8 +153,28 @@ pub type AtomicU32 = Atomic<u32>; pub type AtomicU64 = Atomic<u64>; - pub use self::serial_join as join; - pub use self::serial_scope as scope; + pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB) + where A: FnOnce() -> RA, + B: FnOnce() -> RB + { + (oper_a(), oper_b()) + } + + pub struct SerialScope; + + impl SerialScope { + pub fn spawn<F>(&self, f: F) + where F: FnOnce(&SerialScope) + { + f(self) + } + } + + pub fn scope<F, R>(f: F) -> R + where F: FnOnce(&SerialScope) -> R + { + f(&SerialScope) + } #[macro_export] macro_rules! parallel { @@ -495,18 +492,20 @@ assert!(self.try_set(value).is_none()); } - /// Tries to initialize the inner value by calling the closure while ensuring that no-one else - /// can access the value in the mean time by holding a lock for the duration of the closure. - /// If the value was already initialized the closure is not called and `false` is returned, - /// otherwise if the value from the closure initializes the inner value, `true` is returned + /// Initializes the inner value if it wasn't already done by calling the provided closure. It + /// ensures that no-one else can access the value in the mean time by holding a lock for the + /// duration of the closure. + /// A reference to the inner value is returned. #[inline] - pub fn init_locking<F: FnOnce() -> T>(&self, f: F) -> bool { - let mut lock = self.0.lock(); - if lock.is_some() { - return false; + pub fn init_locking<F: FnOnce() -> T>(&self, f: F) -> &T { + { + let mut lock = self.0.lock(); + if lock.is_none() { + *lock = Some(f()); + } } - *lock = Some(f()); - true + + self.borrow() } /// Tries to initialize the inner value by calling the closure without ensuring that no-one @@ -741,7 +740,7 @@ /// A type which only allows its inner value to be used in one thread. /// It will panic if it is used on multiple threads. -#[derive(Copy, Clone, Hash, Debug, Eq, PartialEq)] +#[derive(Debug)] pub struct OneThread<T> { #[cfg(parallel_compiler)] thread: thread::ThreadId,
diff --git a/src/librustc_data_structures/thin_vec.rs b/src/librustc_data_structures/thin_vec.rs index 6692903..d97da48 100644 --- a/src/librustc_data_structures/thin_vec.rs +++ b/src/librustc_data_structures/thin_vec.rs
@@ -1,9 +1,9 @@ -use crate::stable_hasher::{StableHasher, StableHasherResult, HashStable}; +use crate::stable_hasher::{StableHasher, HashStable}; /// A vector type optimized for cases where this size is usually 0 (cf. `SmallVector`). /// The `Option<Box<..>>` wrapping allows us to represent a zero sized vector with `None`, /// which uses only a single (null) pointer. -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct ThinVec<T>(Option<Box<Vec<T>>>); impl<T> ThinVec<T> { @@ -60,9 +60,7 @@ } impl<T: HashStable<CTX>, CTX> HashStable<CTX> for ThinVec<T> { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { (**self).hash_stable(hcx, hasher) } }
diff --git a/src/librustc_data_structures/tiny_list.rs b/src/librustc_data_structures/tiny_list.rs index ea771d9..371f0f6 100644 --- a/src/librustc_data_structures/tiny_list.rs +++ b/src/librustc_data_structures/tiny_list.rs
@@ -14,7 +14,7 @@ #[cfg(test)] mod tests; -#[derive(Clone, Hash, Debug, PartialEq)] +#[derive(Clone)] pub struct TinyList<T: PartialEq> { head: Option<Element<T>> } @@ -80,7 +80,7 @@ } } -#[derive(Clone, Hash, Debug, PartialEq)] +#[derive(Clone)] struct Element<T: PartialEq> { data: T, next: Option<Box<Element<T>>>,
diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs index ffc964d..a3926c1 100644 --- a/src/librustc_data_structures/transitive_relation.rs +++ b/src/librustc_data_structures/transitive_relation.rs
@@ -1,6 +1,6 @@ -use crate::bit_set::BitMatrix; +use rustc_index::bit_set::BitMatrix; use crate::fx::FxHashMap; -use crate::stable_hasher::{HashStable, StableHasher, StableHasherResult}; +use crate::stable_hasher::{HashStable, StableHasher}; use crate::sync::Lock; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; use std::fmt::Debug; @@ -11,7 +11,7 @@ mod tests; #[derive(Clone, Debug)] -pub struct TransitiveRelation<T: Clone + Debug + Eq + Hash> { +pub struct TransitiveRelation<T: Eq + Hash> { // List of elements. This is used to map from a T to a usize. elements: Vec<T>, @@ -35,7 +35,7 @@ } // HACK(eddyb) manual impl avoids `Default` bound on `T`. -impl<T: Clone + Debug + Eq + Hash> Default for TransitiveRelation<T> { +impl<T: Eq + Hash> Default for TransitiveRelation<T> { fn default() -> Self { TransitiveRelation { elements: Default::default(), @@ -46,7 +46,7 @@ } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, RustcEncodable, RustcDecodable, Debug)] struct Index(usize); #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)] @@ -442,9 +442,7 @@ impl<CTX, T> HashStable<CTX> for TransitiveRelation<T> where T: HashStable<CTX> + Eq + Debug + Clone + Hash { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { // We are assuming here that the relation graph has been built in a // deterministic way and we can just hash it the way it is. let TransitiveRelation { @@ -462,9 +460,7 @@ } impl<CTX> HashStable<CTX> for Edge { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { let Edge { ref source, ref target, @@ -476,9 +472,7 @@ } impl<CTX> HashStable<CTX> for Index { - fn hash_stable<W: StableHasherResult>(&self, - hcx: &mut CTX, - hasher: &mut StableHasher<W>) { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { let Index(idx) = *self; idx.hash_stable(hcx, hasher); }
diff --git a/src/librustc_data_structures/vec_linked_list.rs b/src/librustc_data_structures/vec_linked_list.rs index 0fb8060..7744c30 100644 --- a/src/librustc_data_structures/vec_linked_list.rs +++ b/src/librustc_data_structures/vec_linked_list.rs
@@ -1,4 +1,4 @@ -use crate::indexed_vec::{Idx, IndexVec}; +use rustc_index::vec::{Idx, IndexVec}; pub fn iter<Ls>( first: Option<Ls::LinkIndex>,
diff --git a/src/librustc_data_structures/work_queue.rs b/src/librustc_data_structures/work_queue.rs index 193025a..af63b18 100644 --- a/src/librustc_data_structures/work_queue.rs +++ b/src/librustc_data_structures/work_queue.rs
@@ -1,5 +1,5 @@ -use crate::bit_set::BitSet; -use crate::indexed_vec::Idx; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::Idx; use std::collections::VecDeque; /// A work queue is a handy data structure for tracking work left to
diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 25f67b3..a9e4e6d 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml
@@ -13,10 +13,10 @@ graphviz = { path = "../libgraphviz" } lazy_static = "1.0" log = "0.4" -env_logger = { version = "0.6", default-features = false } +env_logger = { version = "0.7", default-features = false } rustc = { path = "../librustc" } rustc_target = { path = "../librustc_target" } -rustc_ast_borrowck = { path = "../librustc_ast_borrowck" } +rustc_lint = { path = "../librustc_lint" } rustc_data_structures = { path = "../librustc_data_structures" } errors = { path = "../librustc_errors", package = "rustc_errors" } rustc_metadata = { path = "../librustc_metadata" }
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index f99e65b..6e8bc11 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs
@@ -36,11 +36,11 @@ use rustc::session::{early_error, early_warn}; use rustc::lint::Lint; use rustc::lint; +use rustc::middle::cstore::MetadataLoader; use rustc::hir::def_id::LOCAL_CRATE; use rustc::ty::TyCtxt; use rustc::util::common::{set_time_depth, time, print_time_passes_entry, ErrorReported}; use rustc_metadata::locator; -use rustc_metadata::cstore::CStore; use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_interface::interface; use rustc_interface::util::get_codegen_sysroot; @@ -166,7 +166,8 @@ None => return Ok(()), }; - let (sopts, cfg) = config::build_session_options_and_crate_config(&matches); + let sopts = config::build_session_options(&matches); + let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg")); let mut dummy_config = |sopts, cfg, diagnostic_output| { let mut config = interface::Config { @@ -181,6 +182,7 @@ stderr: None, crate_name: None, lint_caps: Default::default(), + register_lints: None, }; callbacks.config(&mut config); config @@ -201,9 +203,13 @@ interface::run_compiler(config, |compiler| { let sopts = &compiler.session().opts; if sopts.describe_lints { + let lint_store = rustc_lint::new_lint_store( + sopts.debugging_opts.no_interleave_lints, + compiler.session().unstable_options(), + ); describe_lints( compiler.session(), - &*compiler.session().lint_store.borrow(), + &lint_store, false ); return; @@ -254,6 +260,7 @@ stderr: None, crate_name: None, lint_caps: Default::default(), + register_lints: None, }; callbacks.config(&mut config); @@ -268,7 +275,7 @@ compiler.output_file(), ).and_then(|| RustcDefaultCalls::list_metadata( sess, - compiler.cstore(), + &*compiler.codegen_backend().metadata_loader(), &matches, compiler.input() )); @@ -296,7 +303,6 @@ ); Ok(()) })?; - return sess.compile_status(); } else { let mut krate = compiler.parse()?.take(); pretty::visit_crate(sess, &mut krate, ppm); @@ -307,8 +313,8 @@ ppm, compiler.output_file().as_ref().map(|p| &**p), ); - return sess.compile_status(); } + return sess.compile_status(); } if callbacks.after_parsing(compiler) == Compilation::Stop { @@ -321,12 +327,14 @@ return sess.compile_status(); } - compiler.register_plugins()?; + { + let (_, _, lint_store) = &*compiler.register_plugins()?.peek(); - // Lint plugins are registered; now we can process command line flags. - if sess.opts.describe_lints { - describe_lints(&sess, &sess.lint_store.borrow(), true); - return sess.compile_status(); + // Lint plugins are registered; now we can process command line flags. + if sess.opts.describe_lints { + describe_lints(&sess, &lint_store, true); + return sess.compile_status(); + } } compiler.expansion()?; @@ -604,7 +612,7 @@ impl RustcDefaultCalls { pub fn list_metadata(sess: &Session, - cstore: &CStore, + metadata_loader: &dyn MetadataLoader, matches: &getopts::Matches, input: &Input) -> Compilation { @@ -616,7 +624,7 @@ let mut v = Vec::new(); locator::list_file_metadata(&sess.target.target, path, - &*cstore.metadata_loader, + metadata_loader, &mut v) .unwrap(); println!("{}", String::from_utf8(v).unwrap()); @@ -701,7 +709,7 @@ let mut cfgs = sess.parse_sess.config.iter().filter_map(|&(name, ref value)| { let gated_cfg = GatedCfg::gate(&ast::MetaItem { path: ast::Path::from_ident(ast::Ident::with_dummy_span(name)), - node: ast::MetaItemKind::Word, + kind: ast::MetaItemKind::Word, span: DUMMY_SP, }); @@ -835,8 +843,7 @@ "); - fn sort_lints(sess: &Session, lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> { - let mut lints: Vec<_> = lints.into_iter().map(|(x, _)| x).collect(); + fn sort_lints(sess: &Session, mut lints: Vec<&'static Lint>) -> Vec<&'static Lint> { // The sort doesn't case-fold but it's doubtful we care. lints.sort_by_cached_key(|x: &&Lint| (x.default_level(sess), x.name)); lints @@ -852,7 +859,7 @@ let (plugin, builtin): (Vec<_>, _) = lint_store.get_lints() .iter() .cloned() - .partition(|&(_, p)| p); + .partition(|&lint| lint.is_plugin); let plugin = sort_lints(sess, plugin); let builtin = sort_lints(sess, builtin); @@ -1232,7 +1239,7 @@ let backtrace = env::var_os("RUST_BACKTRACE").map(|x| &x != "0").unwrap_or(false); if backtrace { - TyCtxt::try_print_query_stack(); + TyCtxt::try_print_query_stack(&handler); } #[cfg(windows)]
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index fa9504e..0de5b70 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs
@@ -2,7 +2,6 @@ use rustc::hir; use rustc::hir::map as hir_map; -use rustc::hir::map::blocks; use rustc::hir::print as pprust_hir; use rustc::hir::def_id::LOCAL_CRATE; use rustc::session::Session; @@ -10,9 +9,6 @@ use rustc::ty::{self, TyCtxt}; use rustc::util::common::ErrorReported; use rustc_interface::util::ReplaceBodyWithLoop; -use rustc_ast_borrowck as borrowck; -use rustc_ast_borrowck::graphviz as borrowck_dot; -use rustc_ast_borrowck::cfg::{self, graphviz::LabelledCFG}; use rustc_mir::util::{write_mir_pretty, write_mir_graphviz}; use syntax::ast; @@ -20,11 +16,9 @@ use syntax::print::{pprust}; use syntax_pos::FileName; -use graphviz as dot; - use std::cell::Cell; use std::fs::File; -use std::io::{self, Write}; +use std::io::Write; use std::option; use std::path::Path; use std::str::FromStr; @@ -49,20 +43,10 @@ } #[derive(Copy, Clone, PartialEq, Debug)] -pub enum PpFlowGraphMode { - Default, - /// Drops the labels from the edges in the flowgraph output. This - /// is mostly for use in the -Z unpretty flowgraph run-make tests, - /// since the labels are largely uninteresting in those cases and - /// have become a pain to maintain. - UnlabelledEdges, -} -#[derive(Copy, Clone, PartialEq, Debug)] pub enum PpMode { PpmSource(PpSourceMode), PpmHir(PpSourceMode), PpmHirTree(PpSourceMode), - PpmFlowGraph(PpFlowGraphMode), PpmMir, PpmMirCFG, } @@ -80,15 +64,14 @@ PpmHir(_) | PpmHirTree(_) | PpmMir | - PpmMirCFG | - PpmFlowGraph(_) => true, + PpmMirCFG => true, PpmSource(PpmTyped) => panic!("invalid state"), } } pub fn needs_analysis(&self) -> bool { match *self { - PpmMir | PpmMirCFG | PpmFlowGraph(_) => true, + PpmMir | PpmMirCFG => true, _ => false, } } @@ -114,13 +97,11 @@ ("hir-tree", true) => PpmHirTree(PpmNormal), ("mir", true) => PpmMir, ("mir-cfg", true) => PpmMirCFG, - ("flowgraph", true) => PpmFlowGraph(PpFlowGraphMode::Default), - ("flowgraph,unlabelled", true) => PpmFlowGraph(PpFlowGraphMode::UnlabelledEdges), _ => { if extended { sess.fatal(&format!("argument to `unpretty` must be one of `normal`, \ - `expanded`, `flowgraph[,unlabelled]=<nodeid>`, \ - `identified`, `expanded,identified`, `everybody_loops`, \ + `expanded`, `identified`, `expanded,identified`, \ + `expanded,hygiene`, `everybody_loops`, \ `hir`, `hir,identified`, `hir,typed`, `hir-tree`, \ `mir` or `mir-cfg`; got {}", name)); @@ -501,24 +482,6 @@ } } -fn gather_flowgraph_variants(sess: &Session) -> Vec<borrowck_dot::Variant> { - let print_loans = sess.opts.debugging_opts.flowgraph_print_loans; - let print_moves = sess.opts.debugging_opts.flowgraph_print_moves; - let print_assigns = sess.opts.debugging_opts.flowgraph_print_assigns; - let print_all = sess.opts.debugging_opts.flowgraph_print_all; - let mut variants = Vec::new(); - if print_all || print_loans { - variants.push(borrowck_dot::Loans); - } - if print_all || print_moves { - variants.push(borrowck_dot::Moves); - } - if print_all || print_assigns { - variants.push(borrowck_dot::Assigns); - } - variants -} - #[derive(Clone, Debug)] pub enum UserIdentifiedItem { ItemViaNode(ast::NodeId), @@ -609,81 +572,6 @@ } } -fn print_flowgraph<'tcx, W: Write>( - variants: Vec<borrowck_dot::Variant>, - tcx: TyCtxt<'tcx>, - code: blocks::Code<'tcx>, - mode: PpFlowGraphMode, - mut out: W, -) -> io::Result<()> { - let body_id = match code { - blocks::Code::Expr(expr) => { - // Find the function this expression is from. - let mut hir_id = expr.hir_id; - loop { - let node = tcx.hir().get(hir_id); - if let Some(n) = hir::map::blocks::FnLikeNode::from_node(node) { - break n.body(); - } - let parent = tcx.hir().get_parent_node(hir_id); - assert_ne!(hir_id, parent); - hir_id = parent; - } - } - blocks::Code::FnLike(fn_like) => fn_like.body(), - }; - let body = tcx.hir().body(body_id); - let cfg = cfg::CFG::new(tcx, &body); - let labelled_edges = mode != PpFlowGraphMode::UnlabelledEdges; - let hir_id = code.id(); - // We have to disassemble the hir_id because name must be ASCII - // alphanumeric. This does not appear in the rendered graph, so it does not - // have to be user friendly. - let name = format!( - "hir_id_{}_{}", - hir_id.owner.index(), - hir_id.local_id.index(), - ); - let lcfg = LabelledCFG { - tcx, - cfg: &cfg, - name, - labelled_edges, - }; - - match code { - _ if variants.is_empty() => { - let r = dot::render(&lcfg, &mut out); - return expand_err_details(r); - } - blocks::Code::Expr(_) => { - tcx.sess.err("--pretty flowgraph with -Z flowgraph-print annotations requires \ - fn-like node id."); - return Ok(()); - } - blocks::Code::FnLike(fn_like) => { - let (bccx, analysis_data) = - borrowck::build_borrowck_dataflow_data_for_fn(tcx, fn_like.body(), &cfg); - - let lcfg = borrowck_dot::DataflowLabeller { - inner: lcfg, - variants, - borrowck_ctxt: &bccx, - analysis_data: &analysis_data, - }; - let r = dot::render(&lcfg, &mut out); - return expand_err_details(r); - } - } - - fn expand_err_details(r: io::Result<()>) -> io::Result<()> { - r.map_err(|ioerr| { - io::Error::new(io::ErrorKind::Other, - format!("graphviz::render failed: {}", ioerr)) - }) - } -} - pub fn visit_crate(sess: &Session, krate: &mut ast::Crate, ppm: PpMode) { if let PpmSource(PpmEveryBodyLoops) = ppm { ReplaceBodyWithLoop::new(sess).visit_crate(krate); @@ -872,55 +760,17 @@ tcx.analysis(LOCAL_CRATE)?; - let mut print = || match ppm { + match ppm { PpmMir | PpmMirCFG => { - if let Some(nodeid) = nodeid { - let def_id = tcx.hir().local_def_id_from_node_id(nodeid); - match ppm { - PpmMir => write_mir_pretty(tcx, Some(def_id), &mut out), - PpmMirCFG => write_mir_graphviz(tcx, Some(def_id), &mut out), - _ => unreachable!(), - }?; - } else { - match ppm { - PpmMir => write_mir_pretty(tcx, None, &mut out), - PpmMirCFG => write_mir_graphviz(tcx, None, &mut out), - _ => unreachable!(), - }?; - } - Ok(()) - } - PpmFlowGraph(mode) => { - let nodeid = - nodeid.expect("`pretty flowgraph=..` needs NodeId (int) or unique path \ - suffix (b::c::d)"); - let hir_id = tcx.hir().node_to_hir_id(nodeid); - let node = tcx.hir().find(hir_id).unwrap_or_else(|| { - tcx.sess.fatal(&format!("`--pretty=flowgraph` couldn't find ID: {}", nodeid)) - }); - - match blocks::Code::from_node(&tcx.hir(), hir_id) { - Some(code) => { - let variants = gather_flowgraph_variants(tcx.sess); - - let out: &mut dyn Write = &mut out; - - print_flowgraph(variants, tcx, code, mode, out) - } - None => { - let message = format!("`--pretty=flowgraph` needs block, fn, or method; \ - got {:?}", - node); - - let hir_id = tcx.hir().node_to_hir_id(nodeid); - tcx.sess.span_fatal(tcx.hir().span(hir_id), &message) - } + let def_id = nodeid.map(|nid| tcx.hir().local_def_id_from_node_id(nid)); + match ppm { + PpmMir => write_mir_pretty(tcx, def_id, &mut out), + PpmMirCFG => write_mir_graphviz(tcx, def_id, &mut out), + _ => unreachable!(), } } _ => unreachable!(), - }; - - print().unwrap(); + }.unwrap(); write_output(out, ofile);
diff --git a/src/librustc_errors/annotate_snippet_emitter_writer.rs b/src/librustc_errors/annotate_snippet_emitter_writer.rs index 0281d10..491bc2a 100644 --- a/src/librustc_errors/annotate_snippet_emitter_writer.rs +++ b/src/librustc_errors/annotate_snippet_emitter_writer.rs
@@ -31,24 +31,28 @@ impl Emitter for AnnotateSnippetEmitterWriter { /// The entry point for the diagnostics generation - fn emit_diagnostic(&mut self, db: &Diagnostic) { - let mut children = db.children.clone(); - let (mut primary_span, suggestions) = self.primary_span_formatted(&db); + fn emit_diagnostic(&mut self, diag: &Diagnostic) { + let mut children = diag.children.clone(); + let (mut primary_span, suggestions) = self.primary_span_formatted(&diag); self.fix_multispans_in_std_macros(&self.source_map, &mut primary_span, &mut children, - &db.level, + &diag.level, self.external_macro_backtrace); - self.emit_messages_default(&db.level, - db.message(), - &db.code, + self.emit_messages_default(&diag.level, + diag.message(), + &diag.code, &primary_span, &children, &suggestions); } + fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> { + self.source_map.as_ref() + } + fn should_show_explain(&self) -> bool { !self.short_message }
diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 3f1b912..5a09898 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs
@@ -34,7 +34,7 @@ pub render_span: Option<MultiSpan>, } -#[derive(PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub struct DiagnosticStyledString(pub Vec<StringPart>); impl DiagnosticStyledString { @@ -60,7 +60,7 @@ } } -#[derive(PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub enum StringPart { Normal(String), Highlighted(String), @@ -152,6 +152,32 @@ self.note_expected_found_extra(label, expected, found, &"", &"") } + pub fn note_unsuccessfull_coercion(&mut self, + expected: DiagnosticStyledString, + found: DiagnosticStyledString) + -> &mut Self + { + let mut msg: Vec<_> = + vec![(format!("required when trying to coerce from type `"), + Style::NoStyle)]; + msg.extend(expected.0.iter() + .map(|x| match *x { + StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), + StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), + })); + msg.push((format!("` to type '"), Style::NoStyle)); + msg.extend(found.0.iter() + .map(|x| match *x { + StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), + StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), + })); + msg.push((format!("`"), Style::NoStyle)); + + // For now, just attach these as notes + self.highlighted_note(msg); + self + } + pub fn note_expected_found_extra(&mut self, label: &dyn fmt::Display, expected: DiagnosticStyledString, @@ -298,9 +324,31 @@ /// * may contain a name of a function, variable, or type, but not whole expressions /// /// See `CodeSuggestion` for more information. - pub fn span_suggestion(&mut self, sp: Span, msg: &str, - suggestion: String, - applicability: Applicability) -> &mut Self { + pub fn span_suggestion( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + self.span_suggestion_with_style( + sp, + msg, + suggestion, + applicability, + SuggestionStyle::ShowCode, + ); + self + } + + pub fn span_suggestion_with_style( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + style: SuggestionStyle, + ) -> &mut Self { self.suggestions.push(CodeSuggestion { substitutions: vec![Substitution { parts: vec![SubstitutionPart { @@ -309,16 +357,37 @@ }], }], msg: msg.to_owned(), - style: SuggestionStyle::ShowCode, + style, applicability, }); self } + pub fn span_suggestion_verbose( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + self.span_suggestion_with_style( + sp, + msg, + suggestion, + applicability, + SuggestionStyle::ShowAlways, + ); + self + } + /// Prints out a message with multiple suggested edits of the code. - pub fn span_suggestions(&mut self, sp: Span, msg: &str, - suggestions: impl Iterator<Item = String>, applicability: Applicability) -> &mut Self - { + pub fn span_suggestions( + &mut self, + sp: Span, + msg: &str, + suggestions: impl Iterator<Item = String>, + applicability: Applicability, + ) -> &mut Self { self.suggestions.push(CodeSuggestion { substitutions: suggestions.map(|snippet| Substitution { parts: vec![SubstitutionPart { @@ -340,17 +409,13 @@ pub fn span_suggestion_short( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability ) -> &mut Self { - self.suggestions.push(CodeSuggestion { - substitutions: vec![Substitution { - parts: vec![SubstitutionPart { - snippet: suggestion, - span: sp, - }], - }], - msg: msg.to_owned(), - style: SuggestionStyle::HideCodeInline, + self.span_suggestion_with_style( + sp, + msg, + suggestion, applicability, - }); + SuggestionStyle::HideCodeInline, + ); self } @@ -363,17 +428,13 @@ pub fn span_suggestion_hidden( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability ) -> &mut Self { - self.suggestions.push(CodeSuggestion { - substitutions: vec![Substitution { - parts: vec![SubstitutionPart { - snippet: suggestion, - span: sp, - }], - }], - msg: msg.to_owned(), - style: SuggestionStyle::HideCodeAlways, + self.span_suggestion_with_style( + sp, + msg, + suggestion, applicability, - }); + SuggestionStyle::HideCodeAlways, + ); self } @@ -384,17 +445,13 @@ pub fn tool_only_span_suggestion( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability ) -> &mut Self { - self.suggestions.push(CodeSuggestion { - substitutions: vec![Substitution { - parts: vec![SubstitutionPart { - snippet: suggestion, - span: sp, - }], - }], - msg: msg.to_owned(), - style: SuggestionStyle::CompletelyHidden, + self.span_suggestion_with_style( + sp, + msg, + suggestion, applicability, - }); + SuggestionStyle::CompletelyHidden, + ); self }
diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index e85388b..40642dd 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs
@@ -1,10 +1,6 @@ -use crate::Diagnostic; -use crate::DiagnosticId; -use crate::DiagnosticStyledString; -use crate::Applicability; +use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString}; +use crate::{Applicability, Level, Handler, StashKey}; -use crate::Level; -use crate::Handler; use std::fmt::{self, Debug}; use std::ops::{Deref, DerefMut}; use std::thread::panicking; @@ -117,18 +113,30 @@ } } - /// Buffers the diagnostic for later emission, unless handler - /// has disabled such buffering. - pub fn buffer(mut self, buffered_diagnostics: &mut Vec<Diagnostic>) { + /// Stashes diagnostic for possible later improvement in a different, + /// later stage of the compiler. The diagnostic can be accessed with + /// the provided `span` and `key` through `.steal_diagnostic` on `Handler`. + /// + /// As with `buffer`, this is unless the handler has disabled such buffering. + pub fn stash(self, span: Span, key: StashKey) { + if let Some((diag, handler)) = self.into_diagnostic() { + handler.stash_diagnostic(span, key, diag); + } + } + + /// Converts the builder to a `Diagnostic` for later emission, + /// unless handler has disabled such buffering. + pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> { if self.0.handler.flags.dont_buffer_diagnostics || self.0.handler.flags.treat_err_as_bug.is_some() { self.emit(); - return; + return None; } - // We need to use `ptr::read` because `DiagnosticBuilder` - // implements `Drop`. + let handler = self.0.handler; + + // We need to use `ptr::read` because `DiagnosticBuilder` implements `Drop`. let diagnostic; unsafe { diagnostic = std::ptr::read(&self.0.diagnostic); @@ -137,7 +145,14 @@ // Logging here is useful to help track down where in logs an error was // actually emitted. debug!("buffer: diagnostic={:?}", diagnostic); - buffered_diagnostics.push(diagnostic); + + Some((diagnostic, handler)) + } + + /// Buffers the diagnostic for later emission, + /// unless handler has disabled such buffering. + pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) { + buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag)); } /// Convenience function for internal use, clients should use one of the @@ -194,6 +209,11 @@ found_extra: &dyn fmt::Display, ) -> &mut Self); + forward!(pub fn note_unsuccessfull_coercion(&mut self, + expected: DiagnosticStyledString, + found: DiagnosticStyledString, + ) -> &mut Self); + forward!(pub fn note(&mut self, msg: &str) -> &mut Self); forward!(pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S,
diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index fc44132..b153f0f 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs
@@ -12,8 +12,8 @@ use syntax_pos::{SourceFile, Span, MultiSpan}; use crate::{ - Level, CodeSuggestion, Diagnostic, SubDiagnostic, - SuggestionStyle, SourceMapperDyn, DiagnosticId, + Level, CodeSuggestion, Diagnostic, SubDiagnostic, pluralise, + SuggestionStyle, SourceMapper, SourceMapperDyn, DiagnosticId, }; use crate::Level::Error; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledString, Style}; @@ -99,8 +99,8 @@ // ``` let mut m = Margin { - whitespace_left: if whitespace_left >= 6 { whitespace_left - 6 } else { 0 }, - span_left: if span_left >= 6 { span_left - 6 } else { 0 }, + whitespace_left: whitespace_left.saturating_sub(6), + span_left: span_left.saturating_sub(6), span_right: span_right + 6, computed_left: 0, computed_right: 0, @@ -125,7 +125,7 @@ } else { self.computed_right }; - right < line_len && line_len > self.computed_left + self.column_width + right < line_len && self.computed_left + self.column_width < line_len } fn compute(&mut self, max_line_len: usize) { @@ -167,12 +167,10 @@ } fn right(&self, line_len: usize) -> usize { - if max(line_len, self.computed_left) - self.computed_left <= self.column_width { - line_len - } else if self.computed_right > line_len { + if line_len.saturating_sub(self.computed_left) <= self.column_width { line_len } else { - self.computed_right + min(line_len, self.computed_right) } } } @@ -182,7 +180,7 @@ /// Emitter trait for emitting errors. pub trait Emitter { /// Emit a structured diagnostic. - fn emit_diagnostic(&mut self, db: &Diagnostic); + fn emit_diagnostic(&mut self, diag: &Diagnostic); /// Emit a notification that an artifact has been output. /// This is currently only supported for the JSON format, @@ -194,6 +192,8 @@ true } + fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>>; + /// Formats the substitutions of the primary_span /// /// The are a lot of conditions to this method, but in short: @@ -206,10 +206,10 @@ /// we return the original `primary_span` and the original suggestions. fn primary_span_formatted<'a>( &mut self, - db: &'a Diagnostic + diag: &'a Diagnostic, ) -> (MultiSpan, &'a [CodeSuggestion]) { - let mut primary_span = db.span.clone(); - if let Some((sugg, rest)) = db.suggestions.split_first() { + let mut primary_span = diag.span.clone(); + if let Some((sugg, rest)) = diag.suggestions.split_first() { if rest.is_empty() && // ^ if there is only one suggestion // don't display multi-suggestions as labels @@ -220,10 +220,14 @@ sugg.msg.split_whitespace().count() < 10 && // don't display multiline suggestions as labels !sugg.substitutions[0].parts[0].snippet.contains('\n') && - // when this style is set we want the suggestion to be a message, not inline - sugg.style != SuggestionStyle::HideCodeAlways && - // trivial suggestion for tooling's sake, never shown - sugg.style != SuggestionStyle::CompletelyHidden + ![ + // when this style is set we want the suggestion to be a message, not inline + SuggestionStyle::HideCodeAlways, + // trivial suggestion for tooling's sake, never shown + SuggestionStyle::CompletelyHidden, + // subtle suggestion, never shown inline + SuggestionStyle::ShowAlways, + ].contains(&sugg.style) { let substitution = &sugg.substitutions[0].parts[0].snippet.trim(); let msg = if substitution.len() == 0 || sugg.style.hide_inline() { @@ -232,7 +236,20 @@ format!("help: {}", sugg.msg) } else { // Show the default suggestion text with the substitution - format!("help: {}: `{}`", sugg.msg, substitution) + format!( + "help: {}{}: `{}`", + sugg.msg, + if self.source_map().map(|sm| is_case_difference( + &**sm, + substitution, + sugg.substitutions[0].parts[0].span, + )).unwrap_or(false) { + " (notice the capitalization)" + } else { + "" + }, + substitution, + ) }; primary_span.push_span_label(sugg.substitutions[0].parts[0].span, msg); @@ -243,10 +260,10 @@ // to be consistent. We could try to figure out if we can // make one (or the first one) inline, but that would give // undue importance to a semi-random suggestion - (primary_span, &db.suggestions) + (primary_span, &diag.suggestions) } } else { - (primary_span, &db.suggestions) + (primary_span, &diag.suggestions) } } @@ -297,81 +314,82 @@ source_map: &Option<Lrc<SourceMapperDyn>>, span: &mut MultiSpan, always_backtrace: bool) -> bool { - let mut spans_updated = false; + let sm = match source_map { + Some(ref sm) => sm, + None => return false, + }; - if let Some(ref sm) = source_map { - let mut before_after: Vec<(Span, Span)> = vec![]; - let mut new_labels: Vec<(Span, String)> = vec![]; + let mut before_after: Vec<(Span, Span)> = vec![]; + let mut new_labels: Vec<(Span, String)> = vec![]; - // First, find all the spans in <*macros> and point instead at their use site - for sp in span.primary_spans() { - if sp.is_dummy() { + // First, find all the spans in <*macros> and point instead at their use site + for sp in span.primary_spans() { + if sp.is_dummy() { + continue; + } + let call_sp = sm.call_span_if_macro(*sp); + if call_sp != *sp && !always_backtrace { + before_after.push((*sp, call_sp)); + } + let backtrace_len = sp.macro_backtrace().len(); + for (i, trace) in sp.macro_backtrace().iter().rev().enumerate() { + // Only show macro locations that are local + // and display them like a span_note + if trace.def_site_span.is_dummy() { continue; } - let call_sp = sm.call_span_if_macro(*sp); - if call_sp != *sp && !always_backtrace { - before_after.push((*sp, call_sp)); + if always_backtrace { + new_labels.push((trace.def_site_span, + format!("in this expansion of `{}`{}", + trace.macro_decl_name, + if backtrace_len > 2 { + // if backtrace_len == 1 it'll be pointed + // at by "in this macro invocation" + format!(" (#{})", i + 1) + } else { + String::new() + }))); } - let backtrace_len = sp.macro_backtrace().len(); - for (i, trace) in sp.macro_backtrace().iter().rev().enumerate() { - // Only show macro locations that are local - // and display them like a span_note - if trace.def_site_span.is_dummy() { - continue; - } - if always_backtrace { - new_labels.push((trace.def_site_span, - format!("in this expansion of `{}`{}", - trace.macro_decl_name, - if backtrace_len > 2 { - // if backtrace_len == 1 it'll be pointed - // at by "in this macro invocation" - format!(" (#{})", i + 1) - } else { - String::new() - }))); - } - // Check to make sure we're not in any <*macros> - if !sm.span_to_filename(trace.def_site_span).is_macros() && - !trace.macro_decl_name.starts_with("desugaring of ") && - !trace.macro_decl_name.starts_with("#[") || - always_backtrace { - new_labels.push((trace.call_site, - format!("in this macro invocation{}", - if backtrace_len > 2 && always_backtrace { - // only specify order when the macro - // backtrace is multiple levels deep - format!(" (#{})", i + 1) - } else { - String::new() - }))); - if !always_backtrace { - break; - } + // Check to make sure we're not in any <*macros> + if !sm.span_to_filename(trace.def_site_span).is_macros() && + !trace.macro_decl_name.starts_with("desugaring of ") && + !trace.macro_decl_name.starts_with("#[") || + always_backtrace { + new_labels.push((trace.call_site, + format!("in this macro invocation{}", + if backtrace_len > 2 && always_backtrace { + // only specify order when the macro + // backtrace is multiple levels deep + format!(" (#{})", i + 1) + } else { + String::new() + }))); + if !always_backtrace { + break; } } } - for (label_span, label_text) in new_labels { - span.push_span_label(label_span, label_text); + } + for (label_span, label_text) in new_labels { + span.push_span_label(label_span, label_text); + } + for sp_label in span.span_labels() { + if sp_label.span.is_dummy() { + continue; } - for sp_label in span.span_labels() { - if sp_label.span.is_dummy() { - continue; - } - if sm.span_to_filename(sp_label.span.clone()).is_macros() && - !always_backtrace - { - let v = sp_label.span.macro_backtrace(); - if let Some(use_site) = v.last() { - before_after.push((sp_label.span.clone(), use_site.call_site.clone())); - } + if sm.span_to_filename(sp_label.span.clone()).is_macros() && + !always_backtrace + { + let v = sp_label.span.macro_backtrace(); + if let Some(use_site) = v.last() { + before_after.push((sp_label.span.clone(), use_site.call_site.clone())); } } - // After we have them, make sure we replace these 'bad' def sites with their use sites - for (before, after) in before_after { - span.replace(before, after); - spans_updated = true; - } + } + // After we have them, make sure we replace these 'bad' def sites with their use sites + let spans_updated = !before_after.is_empty(); + for (before, after) in before_after { + span.replace(before, after); } spans_updated @@ -379,19 +397,23 @@ } impl Emitter for EmitterWriter { - fn emit_diagnostic(&mut self, db: &Diagnostic) { - let mut children = db.children.clone(); - let (mut primary_span, suggestions) = self.primary_span_formatted(&db); + fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> { + self.sm.as_ref() + } + + fn emit_diagnostic(&mut self, diag: &Diagnostic) { + let mut children = diag.children.clone(); + let (mut primary_span, suggestions) = self.primary_span_formatted(&diag); self.fix_multispans_in_std_macros(&self.sm, &mut primary_span, &mut children, - &db.level, + &diag.level, self.external_macro_backtrace); - self.emit_messages_default(&db.level, - &db.styled_message(), - &db.code, + self.emit_messages_default(&diag.level, + &diag.styled_message(), + &diag.code, &primary_span, &children, &suggestions); @@ -593,9 +615,9 @@ let left = margin.left(source_string.len()); // Left trim // Account for unicode characters of width !=0 that were removed. - let left = source_string.chars().take(left).fold(0, |acc, ch| { - acc + unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1) - }); + let left = source_string.chars().take(left) + .map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1)) + .sum(); self.draw_line( buffer, @@ -623,18 +645,16 @@ // 3 | | // 4 | | } // | |_^ test - if line.annotations.len() == 1 { - if let Some(ref ann) = line.annotations.get(0) { - if let AnnotationType::MultilineStart(depth) = ann.annotation_type { - if source_string.chars().take(ann.start_col).all(|c| c.is_whitespace()) { - let style = if ann.is_primary { - Style::UnderlinePrimary - } else { - Style::UnderlineSecondary - }; - buffer.putc(line_offset, width_offset + depth - 1, '/', style); - return vec![(depth, style)]; - } + if let [ann] = &line.annotations[..] { + if let AnnotationType::MultilineStart(depth) = ann.annotation_type { + if source_string.chars().take(ann.start_col).all(|c| c.is_whitespace()) { + let style = if ann.is_primary { + Style::UnderlinePrimary + } else { + Style::UnderlineSecondary + }; + buffer.putc(line_offset, width_offset + depth - 1, '/', style); + return vec![(depth, style)]; } } } @@ -763,11 +783,7 @@ annotations_position.push((p, annotation)); for (j, next) in annotations.iter().enumerate() { if j > i { - let l = if let Some(ref label) = next.label { - label.len() + 2 - } else { - 0 - }; + let l = next.label.as_ref().map_or(0, |label| label.len() + 2); if (overlaps(next, annotation, l) // Do not allow two labels to be in the same // line if they overlap including padding, to // avoid situations like: @@ -797,9 +813,7 @@ } } } - if line_len < p { - line_len = p; - } + line_len = max(line_len, p); } if line_len != 0 { @@ -941,17 +955,9 @@ Style::LabelSecondary }; let (pos, col) = if pos == 0 { - (pos + 1, if annotation.end_col + 1 > left { - annotation.end_col + 1 - left - } else { - 0 - }) + (pos + 1, (annotation.end_col + 1).saturating_sub(left)) } else { - (pos + 2, if annotation.start_col > left { - annotation.start_col - left - } else { - 0 - }) + (pos + 2, annotation.start_col.saturating_sub(left)) }; if let Some(ref label) = annotation.label { buffer.puts(line_offset + pos, code_offset + col, &label, style); @@ -966,9 +972,9 @@ // | | | // | | something about `foo` // | something about `fn foo()` - annotations_position.sort_by(|a, b| { - // Decreasing order. When `a` and `b` are the same length, prefer `Primary`. - (a.1.len(), !a.1.is_primary).cmp(&(b.1.len(), !b.1.is_primary)).reverse() + annotations_position.sort_by_key(|(_, ann)| { + // Decreasing order. When annotations share the same length, prefer `Primary`. + (Reverse(ann.len()), ann.is_primary) }); // Write the underlines. @@ -991,11 +997,7 @@ for p in annotation.start_col..annotation.end_col { buffer.putc( line_offset + 1, - if code_offset + p > left { - code_offset + p - left - } else { - 0 - }, + (code_offset + p).saturating_sub(left), underline, style, ); @@ -1018,41 +1020,37 @@ } fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize { + let sm = match self.sm { + Some(ref sm) => sm, + None => return 0, + }; + let mut max = 0; - if let Some(ref sm) = self.sm { - for primary_span in msp.primary_spans() { - if !primary_span.is_dummy() { - let hi = sm.lookup_char_pos(primary_span.hi()); - if hi.line > max { - max = hi.line; - } - } + for primary_span in msp.primary_spans() { + if !primary_span.is_dummy() { + let hi = sm.lookup_char_pos(primary_span.hi()); + max = (hi.line).max(max); } - if !self.short_message { - for span_label in msp.span_labels() { - if !span_label.span.is_dummy() { - let hi = sm.lookup_char_pos(span_label.span.hi()); - if hi.line > max { - max = hi.line; - } - } + } + if !self.short_message { + for span_label in msp.span_labels() { + if !span_label.span.is_dummy() { + let hi = sm.lookup_char_pos(span_label.span.hi()); + max = (hi.line).max(max); } } } + max } fn get_max_line_num(&mut self, span: &MultiSpan, children: &[SubDiagnostic]) -> usize { - let mut max = 0; - let primary = self.get_multispan_max_line_num(span); - max = if primary > max { primary } else { max }; - - for sub in children { - let sub_result = self.get_multispan_max_line_num(&sub.span); - max = if sub_result > max { primary } else { max }; - } - max + children.iter() + .map(|sub| self.get_multispan_max_line_num(&sub.span)) + .max() + .unwrap_or(0) + .max(primary) } /// Adds a left margin to every line but the first, given a padding length and the label being @@ -1082,14 +1080,12 @@ // `max_line_num_len` let padding = " ".repeat(padding + label.len() + 5); - /// Returns `true` if `style`, or the override if present and the style is `NoStyle`. - fn style_or_override(style: Style, override_style: Option<Style>) -> Style { - if let Some(o) = override_style { - if style == Style::NoStyle { - return o; - } + /// Returns `override` if it is present and `style` is `NoStyle` or `style` otherwise + fn style_or_override(style: Style, override_: Option<Style>) -> Style { + match (style, override_) { + (Style::NoStyle, Some(override_)) => override_, + _ => style, } - style } let mut line_number = 0; @@ -1325,13 +1321,12 @@ for line in &annotated_file.lines { max_line_len = max(max_line_len, annotated_file.file .get_line(line.line_index - 1) - .map(|s| s.len()) - .unwrap_or(0)); + .map_or(0, |s| s.len())); for ann in &line.annotations { span_right_margin = max(span_right_margin, ann.start_col); span_right_margin = max(span_right_margin, ann.end_col); // FIXME: account for labels not in the same line - let label_right = ann.label.as_ref().map(|l| l.len() + 1).unwrap_or(0); + let label_right = ann.label.as_ref().map_or(0, |l| l.len() + 1); label_right_margin = max(label_right_margin, ann.end_col + label_right); } } @@ -1460,146 +1455,154 @@ level: &Level, max_line_num_len: usize, ) -> io::Result<()> { - if let Some(ref sm) = self.sm { - let mut buffer = StyledBuffer::new(); + let sm = match self.sm { + Some(ref sm) => sm, + None => return Ok(()) + }; - // Render the suggestion message - let level_str = level.to_string(); - if !level_str.is_empty() { - buffer.append(0, &level_str, Style::Level(level.clone())); - buffer.append(0, ": ", Style::HeaderMsg); + let mut buffer = StyledBuffer::new(); + + // Render the suggestion message + let level_str = level.to_string(); + if !level_str.is_empty() { + buffer.append(0, &level_str, Style::Level(level.clone())); + buffer.append(0, ": ", Style::HeaderMsg); + } + self.msg_to_buffer( + &mut buffer, + &[(suggestion.msg.to_owned(), Style::NoStyle)], + max_line_num_len, + "suggestion", + Some(Style::HeaderMsg), + ); + + // Render the replacements for each suggestion + let suggestions = suggestion.splice_lines(&**sm); + + let mut row_num = 2; + let mut notice_capitalization = false; + for (complete, parts, only_capitalization) in suggestions.iter().take(MAX_SUGGESTIONS) { + notice_capitalization |= only_capitalization; + // Only show underline if the suggestion spans a single line and doesn't cover the + // entirety of the code output. If you have multiple replacements in the same line + // of code, show the underline. + let show_underline = !(parts.len() == 1 + && parts[0].snippet.trim() == complete.trim()) + && complete.lines().count() == 1; + + let lines = sm.span_to_lines(parts[0].span).unwrap(); + + assert!(!lines.lines.is_empty()); + + let line_start = sm.lookup_char_pos(parts[0].span.lo()).line; + draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1); + let mut line_pos = 0; + let mut lines = complete.lines(); + for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { + // Print the span column to avoid confusion + buffer.puts(row_num, + 0, + &self.maybe_anonymized(line_start + line_pos), + Style::LineNumber); + // print the suggestion + draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); + buffer.append(row_num, line, Style::NoStyle); + line_pos += 1; + row_num += 1; } - self.msg_to_buffer( - &mut buffer, - &[(suggestion.msg.to_owned(), Style::NoStyle)], - max_line_num_len, - "suggestion", - Some(Style::HeaderMsg), - ); - // Render the replacements for each suggestion - let suggestions = suggestion.splice_lines(&**sm); + // This offset and the ones below need to be signed to account for replacement code + // that is shorter than the original code. + let mut offset: isize = 0; + // Only show an underline in the suggestions if the suggestion is not the + // entirety of the code being shown and the displayed code is not multiline. + if show_underline { + draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); + for part in parts { + let span_start_pos = sm.lookup_char_pos(part.span.lo()).col_display; + let span_end_pos = sm.lookup_char_pos(part.span.hi()).col_display; - let mut row_num = 2; - for &(ref complete, ref parts) in suggestions.iter().take(MAX_SUGGESTIONS) { - // Only show underline if the suggestion spans a single line and doesn't cover the - // entirety of the code output. If you have multiple replacements in the same line - // of code, show the underline. - let show_underline = !(parts.len() == 1 - && parts[0].snippet.trim() == complete.trim()) - && complete.lines().count() == 1; + // Do not underline the leading... + let start = part.snippet.len() + .saturating_sub(part.snippet.trim_start().len()); + // ...or trailing spaces. Account for substitutions containing unicode + // characters. + let sub_len: usize = part.snippet.trim().chars() + .map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1)) + .sum(); - let lines = sm.span_to_lines(parts[0].span).unwrap(); - - assert!(!lines.lines.is_empty()); - - let line_start = sm.lookup_char_pos(parts[0].span.lo()).line; - draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1); - let mut line_pos = 0; - let mut lines = complete.lines(); - for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { - // Print the span column to avoid confusion - buffer.puts(row_num, - 0, - &self.maybe_anonymized(line_start + line_pos), - Style::LineNumber); - // print the suggestion - draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); - buffer.append(row_num, line, Style::NoStyle); - line_pos += 1; - row_num += 1; - } - - // This offset and the ones below need to be signed to account for replacement code - // that is shorter than the original code. - let mut offset: isize = 0; - // Only show an underline in the suggestions if the suggestion is not the - // entirety of the code being shown and the displayed code is not multiline. - if show_underline { - draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); - for part in parts { - let span_start_pos = sm.lookup_char_pos(part.span.lo()).col_display; - let span_end_pos = sm.lookup_char_pos(part.span.hi()).col_display; - - // Do not underline the leading... - let start = part.snippet.len() - .saturating_sub(part.snippet.trim_start().len()); - // ...or trailing spaces. Account for substitutions containing unicode - // characters. - let sub_len = part.snippet.trim().chars().fold(0, |acc, ch| { - acc + unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1) - }); - - let underline_start = (span_start_pos + start) as isize + offset; - let underline_end = (span_start_pos + start + sub_len) as isize + offset; - for p in underline_start..underline_end { + let underline_start = (span_start_pos + start) as isize + offset; + let underline_end = (span_start_pos + start + sub_len) as isize + offset; + for p in underline_start..underline_end { + buffer.putc(row_num, + max_line_num_len + 3 + p as usize, + '^', + Style::UnderlinePrimary); + } + // underline removals too + if underline_start == underline_end { + for p in underline_start-1..underline_start+1 { buffer.putc(row_num, max_line_num_len + 3 + p as usize, - '^', - Style::UnderlinePrimary); + '-', + Style::UnderlineSecondary); } - // underline removals too - if underline_start == underline_end { - for p in underline_start-1..underline_start+1 { - buffer.putc(row_num, - max_line_num_len + 3 + p as usize, - '-', - Style::UnderlineSecondary); - } - } - - // length of the code after substitution - let full_sub_len = part.snippet.chars().fold(0, |acc, ch| { - acc + unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1) as isize - }); - - // length of the code to be substituted - let snippet_len = span_end_pos as isize - span_start_pos as isize; - // For multiple substitutions, use the position *after* the previous - // substitutions have happened. - offset += full_sub_len - snippet_len; } - row_num += 1; - } - // if we elided some lines, add an ellipsis - if lines.next().is_some() { - buffer.puts(row_num, max_line_num_len - 1, "...", Style::LineNumber); - } else if !show_underline { - draw_col_separator_no_space(&mut buffer, row_num, max_line_num_len + 1); - row_num += 1; + // length of the code after substitution + let full_sub_len = part.snippet.chars() + .map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1)) + .sum::<usize>() as isize; + + // length of the code to be substituted + let snippet_len = span_end_pos as isize - span_start_pos as isize; + // For multiple substitutions, use the position *after* the previous + // substitutions have happened. + offset += full_sub_len - snippet_len; } + row_num += 1; } - if suggestions.len() > MAX_SUGGESTIONS { - let msg = format!("and {} other candidates", suggestions.len() - MAX_SUGGESTIONS); - buffer.puts(row_num, 0, &msg, Style::NoStyle); + + // if we elided some lines, add an ellipsis + if lines.next().is_some() { + buffer.puts(row_num, max_line_num_len - 1, "...", Style::LineNumber); + } else if !show_underline { + draw_col_separator_no_space(&mut buffer, row_num, max_line_num_len + 1); + row_num += 1; } - emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?; } + if suggestions.len() > MAX_SUGGESTIONS { + let others = suggestions.len() - MAX_SUGGESTIONS; + let msg = format!("and {} other candidate{}", others, pluralise!(others)); + buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle); + } else if notice_capitalization { + let msg = "notice the capitalization difference"; + buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle); + } + emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?; Ok(()) } - fn emit_messages_default(&mut self, - level: &Level, - message: &[(String, Style)], - code: &Option<DiagnosticId>, - span: &MultiSpan, - children: &[SubDiagnostic], - suggestions: &[CodeSuggestion]) { + fn emit_messages_default( + &mut self, + level: &Level, + message: &[(String, Style)], + code: &Option<DiagnosticId>, + span: &MultiSpan, + children: &[SubDiagnostic], + suggestions: &[CodeSuggestion], + ) { let max_line_num_len = if self.ui_testing { ANONYMIZED_LINE_NUM.len() } else { self.get_max_line_num(span, children).to_string().len() }; - match self.emit_message_default(span, - message, - code, - level, - max_line_num_len, - false) { + match self.emit_message_default(span, message, code, level, max_line_num_len, false) { Ok(()) => { - if !children.is_empty() { + if !children.is_empty() || suggestions.iter().any(|s| { + s.style != SuggestionStyle::CompletelyHidden + }) { let mut buffer = StyledBuffer::new(); if !self.short_message { draw_col_separator_no_space(&mut buffer, 0, max_line_num_len + 1); @@ -1733,7 +1736,7 @@ hi.col_display += 1; } - let ann_type = if lo.line != hi.line { + if lo.line != hi.line { let ml = MultilineAnnotation { depth: 1, line_start: lo.line, @@ -1741,34 +1744,27 @@ start_col: lo.col_display, end_col: hi.col_display, is_primary: span_label.is_primary, - label: span_label.label.clone(), + label: span_label.label, overlaps_exactly: false, }; - multiline_annotations.push((lo.file.clone(), ml.clone())); - AnnotationType::Multiline(ml) + multiline_annotations.push((lo.file, ml)); } else { - AnnotationType::Singleline - }; - let ann = Annotation { - start_col: lo.col_display, - end_col: hi.col_display, - is_primary: span_label.is_primary, - label: span_label.label.clone(), - annotation_type: ann_type, - }; - - if !ann.is_multiline() { + let ann = Annotation { + start_col: lo.col_display, + end_col: hi.col_display, + is_primary: span_label.is_primary, + label: span_label.label, + annotation_type: AnnotationType::Singleline, + }; add_annotation_to_file(&mut output, lo.file, lo.line, ann); - } + }; } } // Find overlapping multiline annotations, put them at different depths multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, ml.line_end)); - for item in multiline_annotations.clone() { - let ann = item.1; - for item in multiline_annotations.iter_mut() { - let ref mut a = item.1; + for (_, ann) in multiline_annotations.clone() { + for (_, a) in multiline_annotations.iter_mut() { // Move all other multiline annotations overlapping with this one // one level to the right. if !(ann.same_span(a)) && @@ -1785,9 +1781,7 @@ let mut max_depth = 0; // max overlapping multiline spans for (file, ann) in multiline_annotations { - if ann.depth > max_depth { - max_depth = ann.depth; - } + max_depth = max(max_depth, ann.depth); let mut end_ann = ann.as_end(); if !ann.overlaps_exactly { // avoid output like @@ -1822,10 +1816,9 @@ // Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`). add_annotation_to_file(&mut output, file.clone(), line, ann.as_line()); } - if middle < ann.line_end - 1 { - for line in ann.line_end - 1..ann.line_end { - add_annotation_to_file(&mut output, file.clone(), line, ann.as_line()); - } + let line_end = ann.line_end - 1; + if middle < line_end { + add_annotation_to_file(&mut output, file.clone(), line_end, ann.as_line()); } } else { end_ann.annotation_type = AnnotationType::Singleline; @@ -2064,3 +2057,18 @@ } } } + +/// Whether the original and suggested code are visually similar enough to warrant extra wording. +pub fn is_case_difference(sm: &dyn SourceMapper, suggested: &str, sp: Span) -> bool { + // FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode. + let found = sm.span_to_snippet(sp).unwrap(); + let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z']; + // All the chars that differ in capitalization are confusable (above): + let confusable = found.chars().zip(suggested.chars()).filter(|(f, s)| f != s).all(|(f, s)| { + (ascii_confusables.contains(&f) || ascii_confusables.contains(&s)) + }); + confusable && found.to_lowercase() == suggested.to_lowercase() + // FIXME: We sometimes suggest the same thing we already have, which is a + // bug, but be defensive against that here. + && found != suggested +}
diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 1fe5b71..9743cf0 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs
@@ -13,11 +13,11 @@ use Level::*; -use emitter::{Emitter, EmitterWriter}; +use emitter::{Emitter, EmitterWriter, is_case_difference}; use registry::Registry; use rustc_data_structures::sync::{self, Lrc, Lock}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::StableHasher; use std::borrow::Cow; @@ -37,13 +37,16 @@ mod styled_buffer; mod lock; -use syntax_pos::{BytePos, - Loc, - FileLinesResult, - SourceFile, - FileName, - MultiSpan, - Span}; +use syntax_pos::{ + BytePos, + FileLinesResult, + FileName, + Loc, + MultiSpan, + SourceFile, + Span, + SpanSnippetError, +}; /// Indicates the confidence in the correctness of a suggestion. /// @@ -81,6 +84,8 @@ /// This will *not* show the code if the suggestion is inline *and* the suggested code is /// empty. ShowCode, + /// Always show the suggested code independently. + ShowAlways, } impl SuggestionStyle { @@ -145,6 +150,7 @@ fn lookup_char_pos(&self, pos: BytePos) -> Loc; fn span_to_lines(&self, sp: Span) -> FileLinesResult; fn span_to_string(&self, sp: Span) -> String; + fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError>; fn span_to_filename(&self, sp: Span) -> FileName; fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>; fn call_span_if_macro(&self, sp: Span) -> Span; @@ -153,9 +159,12 @@ } impl CodeSuggestion { - /// Returns the assembled code suggestions and whether they should be shown with an underline. - pub fn splice_lines(&self, cm: &SourceMapperDyn) - -> Vec<(String, Vec<SubstitutionPart>)> { + /// Returns the assembled code suggestions, whether they should be shown with an underline + /// and whether the substitution only differs in capitalization. + pub fn splice_lines( + &self, + cm: &SourceMapperDyn, + ) -> Vec<(String, Vec<SubstitutionPart>, bool)> { use syntax_pos::{CharPos, Pos}; fn push_trailing(buf: &mut String, @@ -230,6 +239,7 @@ prev_hi = cm.lookup_char_pos(part.span.hi()); prev_line = fm.get_line(prev_hi.line - 1); } + let only_capitalization = is_case_difference(cm, &buf, bounding_span); // if the replacement already ends with a newline, don't print the next line if !buf.ends_with('\n') { push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None); @@ -238,7 +248,7 @@ while buf.ends_with('\n') { buf.pop(); } - (buf, substitution.parts) + (buf, substitution.parts, only_capitalization) }).collect() } } @@ -302,6 +312,9 @@ inner: Lock<HandlerInner>, } +/// This inner struct exists to keep it all behind a single lock; +/// this is done to prevent possible deadlocks in a multi-threaded compiler, +/// as well as inconsistent state observation. struct HandlerInner { flags: HandlerFlags, /// The number of errors that have been emitted, including duplicates. @@ -326,6 +339,18 @@ /// this handler. These hashes is used to avoid emitting the same error /// twice. emitted_diagnostics: FxHashSet<u128>, + + /// Stashed diagnostics emitted in one stage of the compiler that may be + /// stolen by other stages (e.g. to improve them and add more information). + /// The stashed diagnostics count towards the total error count. + /// When `.abort_if_errors()` is called, these are also emitted. + stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>, +} + +/// A key denoting where from a diagnostic was stashed. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum StashKey { + ItemNoType, } fn default_track_diagnostic(_: &Diagnostic) {} @@ -354,7 +379,9 @@ impl Drop for HandlerInner { fn drop(&mut self) { - if self.err_count == 0 { + self.emit_stashed_diagnostics(); + + if !self.has_errors() { let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new()); let has_bugs = !bugs.is_empty(); for bug in bugs { @@ -368,57 +395,71 @@ } impl Handler { - pub fn with_tty_emitter(color_config: ColorConfig, - can_emit_warnings: bool, - treat_err_as_bug: Option<usize>, - cm: Option<Lrc<SourceMapperDyn>>) - -> Handler { - Handler::with_tty_emitter_and_flags( + pub fn with_tty_emitter( + color_config: ColorConfig, + can_emit_warnings: bool, + treat_err_as_bug: Option<usize>, + cm: Option<Lrc<SourceMapperDyn>>, + ) -> Self { + Self::with_tty_emitter_and_flags( color_config, cm, HandlerFlags { can_emit_warnings, treat_err_as_bug, .. Default::default() - }) + }, + ) } - pub fn with_tty_emitter_and_flags(color_config: ColorConfig, - cm: Option<Lrc<SourceMapperDyn>>, - flags: HandlerFlags) - -> Handler { + pub fn with_tty_emitter_and_flags( + color_config: ColorConfig, + cm: Option<Lrc<SourceMapperDyn>>, + flags: HandlerFlags, + ) -> Self { let emitter = Box::new(EmitterWriter::stderr( - color_config, cm, false, false, None, flags.external_macro_backtrace)); - Handler::with_emitter_and_flags(emitter, flags) + color_config, + cm, + false, + false, + None, + flags.external_macro_backtrace, + )); + Self::with_emitter_and_flags(emitter, flags) } - pub fn with_emitter(can_emit_warnings: bool, - treat_err_as_bug: Option<usize>, - e: Box<dyn Emitter + sync::Send>) - -> Handler { + pub fn with_emitter( + can_emit_warnings: bool, + treat_err_as_bug: Option<usize>, + emitter: Box<dyn Emitter + sync::Send>, + ) -> Self { Handler::with_emitter_and_flags( - e, + emitter, HandlerFlags { can_emit_warnings, treat_err_as_bug, .. Default::default() - }) + }, + ) } - pub fn with_emitter_and_flags(e: Box<dyn Emitter + sync::Send>, flags: HandlerFlags) -> Handler - { - Handler { + pub fn with_emitter_and_flags( + emitter: Box<dyn Emitter + sync::Send>, + flags: HandlerFlags + ) -> Self { + Self { flags, inner: Lock::new(HandlerInner { flags, err_count: 0, deduplicated_err_count: 0, - emitter: e, + emitter, continue_after_error: true, delayed_span_bugs: Vec::new(), taught_diagnostics: Default::default(), emitted_diagnostic_codes: Default::default(), emitted_diagnostics: Default::default(), + stashed_diagnostics: Default::default(), }), } } @@ -438,43 +479,78 @@ /// NOTE: *do not* call this function from rustc. It is only meant to be called from external /// tools that want to reuse a `Parser` cleaning the previously emitted diagnostics as well as /// the overall count of emitted error diagnostics. - // FIXME: this does not clear inner entirely pub fn reset_err_count(&self) { let mut inner = self.inner.borrow_mut(); - // actually frees the underlying memory (which `clear` would not do) - inner.emitted_diagnostics = Default::default(); - inner.deduplicated_err_count = 0; inner.err_count = 0; + inner.deduplicated_err_count = 0; + + // actually free the underlying memory (which `clear` would not do) + inner.delayed_span_bugs = Default::default(); + inner.taught_diagnostics = Default::default(); + inner.emitted_diagnostic_codes = Default::default(); + inner.emitted_diagnostics = Default::default(); + inner.stashed_diagnostics = Default::default(); } + /// Stash a given diagnostic with the given `Span` and `StashKey` as the key for later stealing. + /// If the diagnostic with this `(span, key)` already exists, this will result in an ICE. + pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) { + let mut inner = self.inner.borrow_mut(); + if let Some(mut old_diag) = inner.stashed_diagnostics.insert((span, key), diag) { + // We are removing a previously stashed diagnostic which should not happen. + old_diag.level = Bug; + old_diag.note(&format!( + "{}:{}: already existing stashed diagnostic with (span = {:?}, key = {:?})", + file!(), line!(), span, key + )); + inner.emit_diag_at_span(old_diag, span); + panic!(ExplicitBug); + } + } + + /// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key. + pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_>> { + self.inner + .borrow_mut() + .stashed_diagnostics + .remove(&(span, key)) + .map(|diag| DiagnosticBuilder::new_diagnostic(self, diag)) + } + + /// Emit all stashed diagnostics. + pub fn emit_stashed_diagnostics(&self) { + self.inner.borrow_mut().emit_stashed_diagnostics(); + } + + /// Construct a dummy builder with `Level::Cancelled`. + /// + /// Using this will neither report anything to the user (e.g. a warning), + /// nor will compilation cancel as a result. pub fn struct_dummy(&self) -> DiagnosticBuilder<'_> { DiagnosticBuilder::new(self, Level::Cancelled, "") } - pub fn struct_span_warn<S: Into<MultiSpan>>(&self, - sp: S, - msg: &str) - -> DiagnosticBuilder<'_> { - let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); - result.set_span(sp); - if !self.flags.can_emit_warnings { - result.cancel(); - } + /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. + pub fn struct_span_warn(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> { + let mut result = self.struct_warn(msg); + result.set_span(span); result } - pub fn struct_span_warn_with_code<S: Into<MultiSpan>>(&self, - sp: S, - msg: &str, - code: DiagnosticId) - -> DiagnosticBuilder<'_> { - let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); - result.set_span(sp); + + /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. + /// Also include a code. + pub fn struct_span_warn_with_code( + &self, + span: impl Into<MultiSpan>, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + let mut result = self.struct_span_warn(span, msg); result.code(code); - if !self.flags.can_emit_warnings { - result.cancel(); - } result } + + /// Construct a builder at the `Warning` level with the `msg`. pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> { let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); if !self.flags.can_emit_warnings { @@ -482,146 +558,154 @@ } result } - pub fn struct_span_err<S: Into<MultiSpan>>(&self, - sp: S, - msg: &str) - -> DiagnosticBuilder<'_> { - let mut result = DiagnosticBuilder::new(self, Level::Error, msg); - result.set_span(sp); + + /// Construct a builder at the `Error` level at the given `span` and with the `msg`. + pub fn struct_span_err(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> { + let mut result = self.struct_err(msg); + result.set_span(span); result } - pub fn struct_span_err_with_code<S: Into<MultiSpan>>(&self, - sp: S, - msg: &str, - code: DiagnosticId) - -> DiagnosticBuilder<'_> { - let mut result = DiagnosticBuilder::new(self, Level::Error, msg); - result.set_span(sp); + + /// Construct a builder at the `Error` level at the given `span`, with the `msg`, and `code`. + pub fn struct_span_err_with_code( + &self, + span: impl Into<MultiSpan>, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + let mut result = self.struct_span_err(span, msg); result.code(code); result } + + /// Construct a builder at the `Error` level with the `msg`. // FIXME: This method should be removed (every error should have an associated error code). pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> { DiagnosticBuilder::new(self, Level::Error, msg) } - pub fn struct_err_with_code( + + /// Construct a builder at the `Error` level with the `msg` and the `code`. + pub fn struct_err_with_code(&self, msg: &str, code: DiagnosticId) -> DiagnosticBuilder<'_> { + let mut result = self.struct_err(msg); + result.code(code); + result + } + + /// Construct a builder at the `Fatal` level at the given `span` and with the `msg`. + pub fn struct_span_fatal( &self, + span: impl Into<MultiSpan>, + msg: &str, + ) -> DiagnosticBuilder<'_> { + let mut result = self.struct_fatal(msg); + result.set_span(span); + result + } + + /// Construct a builder at the `Fatal` level at the given `span`, with the `msg`, and `code`. + pub fn struct_span_fatal_with_code( + &self, + span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId, ) -> DiagnosticBuilder<'_> { - let mut result = DiagnosticBuilder::new(self, Level::Error, msg); + let mut result = self.struct_span_fatal(span, msg); result.code(code); result } - pub fn struct_span_fatal<S: Into<MultiSpan>>(&self, - sp: S, - msg: &str) - -> DiagnosticBuilder<'_> { - let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg); - result.set_span(sp); - result - } - pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>(&self, - sp: S, - msg: &str, - code: DiagnosticId) - -> DiagnosticBuilder<'_> { - let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg); - result.set_span(sp); - result.code(code); - result - } + + /// Construct a builder at the `Error` level with the `msg`. pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> { DiagnosticBuilder::new(self, Level::Fatal, msg) } - pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> FatalError { - self.emit_diagnostic(Diagnostic::new(Fatal, msg).set_span(sp)); - self.abort_if_errors_and_should_abort(); + pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: &str) -> FatalError { + self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span); FatalError } - pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self, - sp: S, - msg: &str, - code: DiagnosticId) - -> FatalError { - self.emit_diagnostic(Diagnostic::new_with_code(Fatal, Some(code), msg).set_span(sp)); - self.abort_if_errors_and_should_abort(); + + pub fn span_fatal_with_code( + &self, + span: impl Into<MultiSpan>, + msg: &str, + code: DiagnosticId, + ) -> FatalError { + self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span); FatalError } - pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { - self.emit_diagnostic(Diagnostic::new(Error, msg).set_span(sp)); - self.abort_if_errors_and_should_abort(); + + pub fn span_err(&self, span: impl Into<MultiSpan>, msg: &str) { + self.emit_diag_at_span(Diagnostic::new(Error, msg), span); } - pub fn mut_span_err<S: Into<MultiSpan>>(&self, - sp: S, - msg: &str) - -> DiagnosticBuilder<'_> { - let mut result = DiagnosticBuilder::new(self, Level::Error, msg); - result.set_span(sp); - result + + pub fn span_err_with_code(&self, span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId) { + self.emit_diag_at_span(Diagnostic::new_with_code(Error, Some(code), msg), span); } - pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { - self.emit_diagnostic(Diagnostic::new_with_code(Error, Some(code), msg).set_span(sp)); - self.abort_if_errors_and_should_abort(); + + pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: &str) { + self.emit_diag_at_span(Diagnostic::new(Warning, msg), span); } - pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { - self.emit_diagnostic(Diagnostic::new(Warning, msg).set_span(sp)); - self.abort_if_errors_and_should_abort(); + + pub fn span_warn_with_code(&self, span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId) { + self.emit_diag_at_span(Diagnostic::new_with_code(Warning, Some(code), msg), span); } - pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { - self.emit_diagnostic(Diagnostic::new_with_code(Warning, Some(code), msg).set_span(sp)); - self.abort_if_errors_and_should_abort(); + + pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: &str) -> ! { + self.inner.borrow_mut().span_bug(span, msg) } - pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! { - self.inner.borrow_mut().span_bug(sp, msg) + + pub fn delay_span_bug(&self, span: impl Into<MultiSpan>, msg: &str) { + self.inner.borrow_mut().delay_span_bug(span, msg) } - pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { - self.inner.borrow_mut().delay_span_bug(sp, msg) + + pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: &str) { + self.emit_diag_at_span(Diagnostic::new(Bug, msg), span); } - pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { - self.emit_diagnostic(Diagnostic::new(Bug, msg).set_span(sp)); - self.abort_if_errors_and_should_abort(); + + pub fn span_note_without_error(&self, span: impl Into<MultiSpan>, msg: &str) { + self.emit_diag_at_span(Diagnostic::new(Note, msg), span); } - pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { - self.emit_diagnostic(Diagnostic::new(Note, msg).set_span(sp)); - self.abort_if_errors_and_should_abort(); - } - pub fn span_note_diag(&self, - sp: Span, - msg: &str) - -> DiagnosticBuilder<'_> { + + pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_> { let mut db = DiagnosticBuilder::new(self, Note, msg); - db.set_span(sp); + db.set_span(span); db } + pub fn failure(&self, msg: &str) { self.inner.borrow_mut().failure(msg); } + pub fn fatal(&self, msg: &str) -> FatalError { self.inner.borrow_mut().fatal(msg) } + pub fn err(&self, msg: &str) { self.inner.borrow_mut().err(msg); } + pub fn warn(&self, msg: &str) { let mut db = DiagnosticBuilder::new(self, Warning, msg); db.emit(); } + pub fn note_without_error(&self, msg: &str) { - let mut db = DiagnosticBuilder::new(self, Note, msg); - db.emit(); + DiagnosticBuilder::new(self, Note, msg).emit(); } + pub fn bug(&self, msg: &str) -> ! { self.inner.borrow_mut().bug(msg) } pub fn err_count(&self) -> usize { - self.inner.borrow().err_count + self.inner.borrow().err_count() } pub fn has_errors(&self) -> bool { - self.err_count() > 0 + self.inner.borrow().has_errors() + } + pub fn has_errors_or_delayed_span_bugs(&self) -> bool { + self.inner.borrow().has_errors_or_delayed_span_bugs() } pub fn print_error_count(&self, registry: &Registry) { @@ -629,13 +713,18 @@ } pub fn abort_if_errors(&self) { - self.inner.borrow().abort_if_errors() + self.inner.borrow_mut().abort_if_errors() } pub fn abort_if_errors_and_should_abort(&self) { - self.inner.borrow().abort_if_errors_and_should_abort() + self.inner.borrow_mut().abort_if_errors_and_should_abort() } + /// `true` if we haven't taught a diagnostic with this code already. + /// The caller must then teach the user about such a diagnostic. + /// + /// Used to suppress emitting the same error multiple times with extended explanation when + /// calling `-Zteach`. pub fn must_teach(&self, code: &DiagnosticId) -> bool { self.inner.borrow_mut().must_teach(code) } @@ -648,6 +737,12 @@ self.inner.borrow_mut().emit_diagnostic(diagnostic) } + fn emit_diag_at_span(&self, mut diag: Diagnostic, sp: impl Into<MultiSpan>) { + let mut inner = self.inner.borrow_mut(); + inner.emit_diagnostic(diag.set_span(sp)); + inner.abort_if_errors_and_should_abort(); + } + pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) { self.inner.borrow_mut().emit_artifact_notification(path, artifact_type) } @@ -658,11 +753,6 @@ } impl HandlerInner { - /// `true` if we haven't taught a diagnostic with this code already. - /// The caller must then teach the user about such a diagnostic. - /// - /// Used to suppress emitting the same error multiple times with extended explanation when - /// calling `-Zteach`. fn must_teach(&mut self, code: &DiagnosticId) -> bool { self.taught_diagnostics.insert(code.clone()) } @@ -671,6 +761,12 @@ self.emitter.emit_diagnostic(&db); } + /// Emit all stashed diagnostics. + fn emit_stashed_diagnostics(&mut self) { + let diags = self.stashed_diagnostics.drain(..).map(|x| x.1).collect::<Vec<_>>(); + diags.iter().for_each(|diag| self.emit_diagnostic(diag)); + } + fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) { if diagnostic.cancelled() { return; @@ -713,10 +809,12 @@ } fn treat_err_as_bug(&self) -> bool { - self.flags.treat_err_as_bug.map(|c| self.err_count >= c).unwrap_or(false) + self.flags.treat_err_as_bug.map(|c| self.err_count() >= c).unwrap_or(false) } fn print_error_count(&mut self, registry: &Registry) { + self.emit_stashed_diagnostics(); + let s = match self.deduplicated_err_count { 0 => return, 1 => "aborting due to previous error".to_string(), @@ -760,25 +858,44 @@ } } - fn abort_if_errors_and_should_abort(&self) { - if self.err_count > 0 && !self.continue_after_error { + fn err_count(&self) -> usize { + self.err_count + self.stashed_diagnostics.len() + } + + fn has_errors(&self) -> bool { + self.err_count() > 0 + } + fn has_errors_or_delayed_span_bugs(&self) -> bool { + self.has_errors() || !self.delayed_span_bugs.is_empty() + } + + fn abort_if_errors_and_should_abort(&mut self) { + self.emit_stashed_diagnostics(); + + if self.has_errors() && !self.continue_after_error { FatalError.raise(); } } - fn abort_if_errors(&self) { - if self.err_count > 0 { + fn abort_if_errors(&mut self) { + self.emit_stashed_diagnostics(); + + if self.has_errors() { FatalError.raise(); } } - fn span_bug<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> ! { - self.emit_diagnostic(Diagnostic::new(Bug, msg).set_span(sp)); - self.abort_if_errors_and_should_abort(); + fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> ! { + self.emit_diag_at_span(Diagnostic::new(Bug, msg), sp); panic!(ExplicitBug); } - fn delay_span_bug<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) { + fn emit_diag_at_span(&mut self, mut diag: Diagnostic, sp: impl Into<MultiSpan>) { + self.emit_diagnostic(diag.set_span(sp)); + self.abort_if_errors_and_should_abort(); + } + + fn delay_span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) { if self.treat_err_as_bug() { // FIXME: don't abort here if report_delayed_bugs is off self.span_bug(sp, msg); @@ -793,18 +910,20 @@ } fn fatal(&mut self, msg: &str) -> FatalError { - if self.treat_err_as_bug() { - self.bug(msg); - } - self.emit_diagnostic(&Diagnostic::new(Fatal, msg)); + self.emit_error(Fatal, msg); FatalError } fn err(&mut self, msg: &str) { + self.emit_error(Error, msg); + } + + /// Emit an error; level should be `Error` or `Fatal`. + fn emit_error(&mut self, level: Level, msg: &str,) { if self.treat_err_as_bug() { self.bug(msg); } - self.emit_diagnostic(&Diagnostic::new(Error, msg)); + self.emit_diagnostic(&Diagnostic::new(level, msg)); } fn bug(&mut self, msg: &str) -> ! { @@ -826,7 +945,7 @@ fn panic_if_treat_err_as_bug(&self) { if self.treat_err_as_bug() { - let s = match (self.err_count, self.flags.treat_err_as_bug.unwrap_or(0)) { + let s = match (self.err_count(), self.flags.treat_err_as_bug.unwrap_or(0)) { (0, _) => return, (1, 1) => "aborting due to `-Z treat-err-as-bug=1`".to_string(), (1, _) => return,
diff --git a/src/librustc_errors/styled_buffer.rs b/src/librustc_errors/styled_buffer.rs index 6e03618..b12ab9e 100644 --- a/src/librustc_errors/styled_buffer.rs +++ b/src/librustc_errors/styled_buffer.rs
@@ -111,7 +111,7 @@ pub fn prepend(&mut self, line: usize, string: &str, style: Style) { self.ensure_lines(line); - let string_len = string.len(); + let string_len = string.chars().count(); // Push the old content over to make room for new content for _ in 0..string_len {
diff --git a/src/librustc_incremental/assert_module_sources.rs b/src/librustc_incremental/assert_module_sources.rs index e08eeaf..ca035d0 100644 --- a/src/librustc_incremental/assert_module_sources.rs +++ b/src/librustc_incremental/assert_module_sources.rs
@@ -27,7 +27,7 @@ use rustc::ty::TyCtxt; use std::collections::BTreeSet; use syntax::ast; -use syntax::symbol::{InternedString, Symbol, sym}; +use syntax::symbol::{Symbol, sym}; use rustc::ich::{ATTR_PARTITION_REUSED, ATTR_PARTITION_CODEGENED, ATTR_EXPECTED_CGU_REUSE}; @@ -45,8 +45,8 @@ .collect_and_partition_mono_items(LOCAL_CRATE) .1 .iter() - .map(|cgu| *cgu.name()) - .collect::<BTreeSet<InternedString>>(); + .map(|cgu| cgu.name()) + .collect::<BTreeSet<Symbol>>(); let ams = AssertModuleSource { tcx, @@ -61,7 +61,7 @@ struct AssertModuleSource<'tcx> { tcx: TyCtxt<'tcx>, - available_cgus: BTreeSet<InternedString>, + available_cgus: BTreeSet<Symbol>, } impl AssertModuleSource<'tcx> {
diff --git a/src/librustc_incremental/persist/README.md b/src/librustc_incremental/persist/README.md index 95e0940..8131d28 100644 --- a/src/librustc_incremental/persist/README.md +++ b/src/librustc_incremental/persist/README.md
@@ -1,13 +1,3 @@ -This is the code to load/save the dependency graph. Loading is assumed -to run early in compilation, and saving at the very end. When loading, -the basic idea is that we will load up the dependency graph from the -previous compilation and compare the hashes of our HIR nodes to the -hashes of the HIR nodes that existed at the time. For each node whose -hash has changed, or which no longer exists in the new HIR, we can -remove that node from the old graph along with any nodes that depend -on it. Then we add what's left to the new graph (if any such nodes or -edges already exist, then there would be no effect, but since we do -this first thing, they do not). +For info on how the incremental compilation works, see the [rustc guide]. - - +[rustc guide]: https://rust-lang.github.io/rustc-guide/query.html
diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs index 837aa93..abe0ffb 100644 --- a/src/librustc_incremental/persist/dirty_clean.rs +++ b/src/librustc_incremental/persist/dirty_clean.rs
@@ -327,7 +327,7 @@ let node = self.tcx.hir().get(item_id); let (name, labels) = match node { HirNode::Item(item) => { - match item.node { + match item.kind { // note: these are in the same order as hir::Item_; // FIXME(michaelwoerister): do commented out ones @@ -391,20 +391,20 @@ &format!( "clean/dirty auto-assertions not yet defined \ for Node::Item.node={:?}", - item.node + item.kind ) ), } }, HirNode::TraitItem(item) => { - match item.node { + match item.kind { TraitItemKind::Method(..) => ("Node::TraitItem", LABELS_FN_IN_TRAIT), TraitItemKind::Const(..) => ("NodeTraitConst", LABELS_CONST_IN_TRAIT), TraitItemKind::Type(..) => ("NodeTraitType", LABELS_CONST_IN_TRAIT), } }, HirNode::ImplItem(item) => { - match item.node { + match item.kind { ImplItemKind::Method(..) => ("Node::ImplItem", LABELS_FN_IN_IMPL), ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL), ImplItemKind::TyAlias(..) => ("NodeImplType", LABELS_CONST_IN_IMPL),
diff --git a/src/librustc_incremental/persist/fs.rs b/src/librustc_incremental/persist/fs.rs index 511175d..cbebdeb 100644 --- a/src/librustc_incremental/persist/fs.rs +++ b/src/librustc_incremental/persist/fs.rs
@@ -307,7 +307,7 @@ let incr_comp_session_dir: PathBuf = sess.incr_comp_session_dir().clone(); - if sess.has_errors() { + if sess.has_errors_or_delayed_span_bugs() { // If there have been any errors during compilation, we don't want to // publish this session directory. Rather, we'll just delete it.
diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 90aefb0..673da52 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs
@@ -101,6 +101,7 @@ // before we fire the background thread. let time_passes = sess.time_passes(); + let prof = sess.prof.clone(); if sess.opts.incremental.is_none() { // No incremental compilation. @@ -160,7 +161,9 @@ } MaybeAsync::Async(std::thread::spawn(move || { - time_ext(time_passes, None, "background load prev dep-graph", move || { + time_ext(time_passes, "background load prev dep-graph", move || { + let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph"); + match load_data(report_incremental_info, &path) { LoadResult::DataOutOfDate => LoadResult::DataOutOfDate, LoadResult::Error { message } => LoadResult::Error { message }, @@ -198,6 +201,8 @@ return OnDiskCache::new_empty(sess.source_map()); } + let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache"); + match load_data(sess.opts.debugging_opts.incremental_info, &query_cache_path(sess)) { LoadResult::Ok{ data: (bytes, start_pos) } => OnDiskCache::new(sess, bytes, start_pos), _ => OnDiskCache::new_empty(sess.source_map())
diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs index 13e2c5d..ecc66e6 100644 --- a/src/librustc_incremental/persist/save.rs +++ b/src/librustc_incremental/persist/save.rs
@@ -22,12 +22,18 @@ if sess.opts.incremental.is_none() { return; } + // This is going to be deleted in finalize_session_directory, so let's not create it + if sess.has_errors_or_delayed_span_bugs() { + return; + } let query_cache_path = query_cache_path(sess); let dep_graph_path = dep_graph_path(sess); join(move || { if tcx.sess.opts.debugging_opts.incremental_queries { + let _timer = tcx.prof.generic_activity("incr_comp_persist_result_cache"); + time(sess, "persist query result cache", || { save_in(sess, query_cache_path, @@ -36,6 +42,8 @@ } }, || { time(sess, "persist dep-graph", || { + let _timer = tcx.prof.generic_activity("incr_comp_persist_dep_graph"); + save_in(sess, dep_graph_path, |e| { @@ -56,6 +64,10 @@ if sess.opts.incremental.is_none() { return; } + // This is going to be deleted in finalize_session_directory, so let's not create it + if sess.has_errors_or_delayed_span_bugs() { + return; + } debug!("save_work_product_index()"); dep_graph.assert_ignored(); @@ -135,6 +147,7 @@ // Encode the graph data. let serialized_graph = time(tcx.sess, "getting serialized graph", || { + let _timer = tcx.prof.generic_activity("incr_comp_serialize_dep_graph"); tcx.dep_graph.serialize() }); @@ -214,6 +227,7 @@ } time(tcx.sess, "encoding serialized graph", || { + let _timer = tcx.prof.generic_activity("incr_comp_encode_serialized_dep_graph"); serialized_graph.encode(encoder).unwrap(); }); } @@ -235,6 +249,8 @@ fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut Encoder) { time(tcx.sess, "serialize query result cache", || { + let _timer = tcx.prof.generic_activity("incr_comp_serialize_result_cache"); + tcx.serialize_query_result_cache(encoder).unwrap(); }) }
diff --git a/src/librustc_index/Cargo.toml b/src/librustc_index/Cargo.toml new file mode 100644 index 0000000..b1ebc95 --- /dev/null +++ b/src/librustc_index/Cargo.toml
@@ -0,0 +1,14 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_index" +version = "0.0.0" +edition = "2018" + +[lib] +name = "rustc_index" +path = "lib.rs" +doctest = false + +[dependencies] +rustc_serialize = { path = "../libserialize", package = "serialize" } +smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
diff --git a/src/librustc_index/bit_set.rs b/src/librustc_index/bit_set.rs new file mode 100644 index 0000000..9ed5ef5 --- /dev/null +++ b/src/librustc_index/bit_set.rs
@@ -0,0 +1,1008 @@ +use crate::vec::{Idx, IndexVec}; +use smallvec::SmallVec; +use std::fmt; +use std::iter; +use std::marker::PhantomData; +use std::mem; +use std::slice; + +#[cfg(test)] +mod tests; + +pub type Word = u64; +pub const WORD_BYTES: usize = mem::size_of::<Word>(); +pub const WORD_BITS: usize = WORD_BYTES * 8; + +/// A fixed-size bitset type with a dense representation. +/// +/// NOTE: Use [`GrowableBitSet`] if you need support for resizing after creation. +/// +/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also +/// just be `usize`. +/// +/// All operations that involve an element will panic if the element is equal +/// to or greater than the domain size. All operations that involve two bitsets +/// will panic if the bitsets have differing domain sizes. +/// +/// [`GrowableBitSet`]: struct.GrowableBitSet.html +#[derive(Clone, Eq, PartialEq, RustcDecodable, RustcEncodable)] +pub struct BitSet<T: Idx> { + domain_size: usize, + words: Vec<Word>, + marker: PhantomData<T>, +} + +impl<T: Idx> BitSet<T> { + /// Creates a new, empty bitset with a given `domain_size`. + #[inline] + pub fn new_empty(domain_size: usize) -> BitSet<T> { + let num_words = num_words(domain_size); + BitSet { + domain_size, + words: vec![0; num_words], + marker: PhantomData, + } + } + + /// Creates a new, filled bitset with a given `domain_size`. + #[inline] + pub fn new_filled(domain_size: usize) -> BitSet<T> { + let num_words = num_words(domain_size); + let mut result = BitSet { + domain_size, + words: vec![!0; num_words], + marker: PhantomData, + }; + result.clear_excess_bits(); + result + } + + /// Gets the domain size. + pub fn domain_size(&self) -> usize { + self.domain_size + } + + /// Clear all elements. + #[inline] + pub fn clear(&mut self) { + for word in &mut self.words { + *word = 0; + } + } + + /// Clear excess bits in the final word. + fn clear_excess_bits(&mut self) { + let num_bits_in_final_word = self.domain_size % WORD_BITS; + if num_bits_in_final_word > 0 { + let mask = (1 << num_bits_in_final_word) - 1; + let final_word_idx = self.words.len() - 1; + self.words[final_word_idx] &= mask; + } + } + + /// Efficiently overwrite `self` with `other`. + pub fn overwrite(&mut self, other: &BitSet<T>) { + assert!(self.domain_size == other.domain_size); + self.words.clone_from_slice(&other.words); + } + + /// Count the number of set bits in the set. + pub fn count(&self) -> usize { + self.words.iter().map(|e| e.count_ones() as usize).sum() + } + + /// Returns `true` if `self` contains `elem`. + #[inline] + pub fn contains(&self, elem: T) -> bool { + assert!(elem.index() < self.domain_size); + let (word_index, mask) = word_index_and_mask(elem); + (self.words[word_index] & mask) != 0 + } + + /// Is `self` is a (non-strict) superset of `other`? + #[inline] + pub fn superset(&self, other: &BitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size); + self.words.iter().zip(&other.words).all(|(a, b)| (a & b) == *b) + } + + /// Is the set empty? + #[inline] + pub fn is_empty(&self) -> bool { + self.words.iter().all(|a| *a == 0) + } + + /// Insert `elem`. Returns whether the set has changed. + #[inline] + pub fn insert(&mut self, elem: T) -> bool { + assert!(elem.index() < self.domain_size); + let (word_index, mask) = word_index_and_mask(elem); + let word_ref = &mut self.words[word_index]; + let word = *word_ref; + let new_word = word | mask; + *word_ref = new_word; + new_word != word + } + + /// Sets all bits to true. + pub fn insert_all(&mut self) { + for word in &mut self.words { + *word = !0; + } + self.clear_excess_bits(); + } + + /// Returns `true` if the set has changed. + #[inline] + pub fn remove(&mut self, elem: T) -> bool { + assert!(elem.index() < self.domain_size); + let (word_index, mask) = word_index_and_mask(elem); + let word_ref = &mut self.words[word_index]; + let word = *word_ref; + let new_word = word & !mask; + *word_ref = new_word; + new_word != word + } + + /// Sets `self = self | other` and returns `true` if `self` changed + /// (i.e., if new bits were added). + pub fn union(&mut self, other: &impl UnionIntoBitSet<T>) -> bool { + other.union_into(self) + } + + /// Sets `self = self - other` and returns `true` if `self` changed. + /// (i.e., if any bits were removed). + pub fn subtract(&mut self, other: &impl SubtractFromBitSet<T>) -> bool { + other.subtract_from(self) + } + + /// Sets `self = self & other` and return `true` if `self` changed. + /// (i.e., if any bits were removed). + pub fn intersect(&mut self, other: &BitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size); + bitwise(&mut self.words, &other.words, |a, b| { a & b }) + } + + /// Gets a slice of the underlying words. + pub fn words(&self) -> &[Word] { + &self.words + } + + /// Iterates over the indices of set bits in a sorted order. + #[inline] + pub fn iter(&self) -> BitIter<'_, T> { + BitIter::new(&self.words) + } + + /// Duplicates the set as a hybrid set. + pub fn to_hybrid(&self) -> HybridBitSet<T> { + // Note: we currently don't bother trying to make a Sparse set. + HybridBitSet::Dense(self.to_owned()) + } + + /// Set `self = self | other`. In contrast to `union` returns `true` if the set contains at + /// least one bit that is not in `other` (i.e. `other` is not a superset of `self`). + /// + /// This is an optimization for union of a hybrid bitset. + fn reverse_union_sparse(&mut self, sparse: &SparseBitSet<T>) -> bool { + assert!(sparse.domain_size == self.domain_size); + self.clear_excess_bits(); + + let mut not_already = false; + // Index of the current word not yet merged. + let mut current_index = 0; + // Mask of bits that came from the sparse set in the current word. + let mut new_bit_mask = 0; + for (word_index, mask) in sparse.iter().map(|x| word_index_and_mask(*x)) { + // Next bit is in a word not inspected yet. + if word_index > current_index { + self.words[current_index] |= new_bit_mask; + // Were there any bits in the old word that did not occur in the sparse set? + not_already |= (self.words[current_index] ^ new_bit_mask) != 0; + // Check all words we skipped for any set bit. + not_already |= self.words[current_index+1..word_index].iter().any(|&x| x != 0); + // Update next word. + current_index = word_index; + // Reset bit mask, no bits have been merged yet. + new_bit_mask = 0; + } + // Add bit and mark it as coming from the sparse set. + // self.words[word_index] |= mask; + new_bit_mask |= mask; + } + self.words[current_index] |= new_bit_mask; + // Any bits in the last inspected word that were not in the sparse set? + not_already |= (self.words[current_index] ^ new_bit_mask) != 0; + // Any bits in the tail? Note `clear_excess_bits` before. + not_already |= self.words[current_index+1..].iter().any(|&x| x != 0); + + not_already + } +} + +/// This is implemented by all the bitsets so that BitSet::union() can be +/// passed any type of bitset. +pub trait UnionIntoBitSet<T: Idx> { + // Performs `other = other | self`. + fn union_into(&self, other: &mut BitSet<T>) -> bool; +} + +/// This is implemented by all the bitsets so that BitSet::subtract() can be +/// passed any type of bitset. +pub trait SubtractFromBitSet<T: Idx> { + // Performs `other = other - self`. + fn subtract_from(&self, other: &mut BitSet<T>) -> bool; +} + +impl<T: Idx> UnionIntoBitSet<T> for BitSet<T> { + fn union_into(&self, other: &mut BitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size); + bitwise(&mut other.words, &self.words, |a, b| { a | b }) + } +} + +impl<T: Idx> SubtractFromBitSet<T> for BitSet<T> { + fn subtract_from(&self, other: &mut BitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size); + bitwise(&mut other.words, &self.words, |a, b| { a & !b }) + } +} + +impl<T: Idx> fmt::Debug for BitSet<T> { + fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { + w.debug_list() + .entries(self.iter()) + .finish() + } +} + +impl<T: Idx> ToString for BitSet<T> { + fn to_string(&self) -> String { + let mut result = String::new(); + let mut sep = '['; + + // Note: this is a little endian printout of bytes. + + // i tracks how many bits we have printed so far. + let mut i = 0; + for word in &self.words { + let mut word = *word; + for _ in 0..WORD_BYTES { // for each byte in `word`: + let remain = self.domain_size - i; + // If less than a byte remains, then mask just that many bits. + let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF }; + assert!(mask <= 0xFF); + let byte = word & mask; + + result.push_str(&format!("{}{:02x}", sep, byte)); + + if remain <= 8 { break; } + word >>= 8; + i += 8; + sep = '-'; + } + sep = '|'; + } + result.push(']'); + + result + } +} + +pub struct BitIter<'a, T: Idx> { + /// A copy of the current word, but with any already-visited bits cleared. + /// (This lets us use `trailing_zeros()` to find the next set bit.) When it + /// is reduced to 0, we move onto the next word. + word: Word, + + /// The offset (measured in bits) of the current word. + offset: usize, + + /// Underlying iterator over the words. + iter: slice::Iter<'a, Word>, + + marker: PhantomData<T> +} + +impl<'a, T: Idx> BitIter<'a, T> { + #[inline] + fn new(words: &'a [Word]) -> BitIter<'a, T> { + // We initialize `word` and `offset` to degenerate values. On the first + // call to `next()` we will fall through to getting the first word from + // `iter`, which sets `word` to the first word (if there is one) and + // `offset` to 0. Doing it this way saves us from having to maintain + // additional state about whether we have started. + BitIter { + word: 0, + offset: std::usize::MAX - (WORD_BITS - 1), + iter: words.iter(), + marker: PhantomData, + } + } +} + +impl<'a, T: Idx> Iterator for BitIter<'a, T> { + type Item = T; + fn next(&mut self) -> Option<T> { + loop { + if self.word != 0 { + // Get the position of the next set bit in the current word, + // then clear the bit. + let bit_pos = self.word.trailing_zeros() as usize; + let bit = 1 << bit_pos; + self.word ^= bit; + return Some(T::new(bit_pos + self.offset)) + } + + // Move onto the next word. `wrapping_add()` is needed to handle + // the degenerate initial value given to `offset` in `new()`. + let word = self.iter.next()?; + self.word = *word; + self.offset = self.offset.wrapping_add(WORD_BITS); + } + } +} + +#[inline] +fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool + where Op: Fn(Word, Word) -> Word +{ + assert_eq!(out_vec.len(), in_vec.len()); + let mut changed = false; + for (out_elem, in_elem) in out_vec.iter_mut().zip(in_vec.iter()) { + let old_val = *out_elem; + let new_val = op(old_val, *in_elem); + *out_elem = new_val; + changed |= old_val != new_val; + } + changed +} + +const SPARSE_MAX: usize = 8; + +/// A fixed-size bitset type with a sparse representation and a maximum of +/// `SPARSE_MAX` elements. The elements are stored as a sorted `SmallVec` with +/// no duplicates; although `SmallVec` can spill its elements to the heap, that +/// never happens within this type because of the `SPARSE_MAX` limit. +/// +/// This type is used by `HybridBitSet`; do not use directly. +#[derive(Clone, Debug)] +pub struct SparseBitSet<T: Idx> { + domain_size: usize, + elems: SmallVec<[T; SPARSE_MAX]>, +} + +impl<T: Idx> SparseBitSet<T> { + fn new_empty(domain_size: usize) -> Self { + SparseBitSet { + domain_size, + elems: SmallVec::new() + } + } + + fn len(&self) -> usize { + self.elems.len() + } + + fn is_empty(&self) -> bool { + self.elems.len() == 0 + } + + fn contains(&self, elem: T) -> bool { + assert!(elem.index() < self.domain_size); + self.elems.contains(&elem) + } + + fn insert(&mut self, elem: T) -> bool { + assert!(elem.index() < self.domain_size); + let changed = if let Some(i) = self.elems.iter().position(|&e| e >= elem) { + if self.elems[i] == elem { + // `elem` is already in the set. + false + } else { + // `elem` is smaller than one or more existing elements. + self.elems.insert(i, elem); + true + } + } else { + // `elem` is larger than all existing elements. + self.elems.push(elem); + true + }; + assert!(self.len() <= SPARSE_MAX); + changed + } + + fn remove(&mut self, elem: T) -> bool { + assert!(elem.index() < self.domain_size); + if let Some(i) = self.elems.iter().position(|&e| e == elem) { + self.elems.remove(i); + true + } else { + false + } + } + + fn to_dense(&self) -> BitSet<T> { + let mut dense = BitSet::new_empty(self.domain_size); + for elem in self.elems.iter() { + dense.insert(*elem); + } + dense + } + + fn iter(&self) -> slice::Iter<'_, T> { + self.elems.iter() + } +} + +impl<T: Idx> UnionIntoBitSet<T> for SparseBitSet<T> { + fn union_into(&self, other: &mut BitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size); + let mut changed = false; + for elem in self.iter() { + changed |= other.insert(*elem); + } + changed + } +} + +impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> { + fn subtract_from(&self, other: &mut BitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size); + let mut changed = false; + for elem in self.iter() { + changed |= other.remove(*elem); + } + changed + } +} + +/// A fixed-size bitset type with a hybrid representation: sparse when there +/// are up to a `SPARSE_MAX` elements in the set, but dense when there are more +/// than `SPARSE_MAX`. +/// +/// This type is especially efficient for sets that typically have a small +/// number of elements, but a large `domain_size`, and are cleared frequently. +/// +/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also +/// just be `usize`. +/// +/// All operations that involve an element will panic if the element is equal +/// to or greater than the domain size. All operations that involve two bitsets +/// will panic if the bitsets have differing domain sizes. +#[derive(Clone, Debug)] +pub enum HybridBitSet<T: Idx> { + Sparse(SparseBitSet<T>), + Dense(BitSet<T>), +} + +impl<T: Idx> HybridBitSet<T> { + pub fn new_empty(domain_size: usize) -> Self { + HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size)) + } + + fn domain_size(&self) -> usize { + match self { + HybridBitSet::Sparse(sparse) => sparse.domain_size, + HybridBitSet::Dense(dense) => dense.domain_size, + } + } + + pub fn clear(&mut self) { + let domain_size = self.domain_size(); + *self = HybridBitSet::new_empty(domain_size); + } + + pub fn contains(&self, elem: T) -> bool { + match self { + HybridBitSet::Sparse(sparse) => sparse.contains(elem), + HybridBitSet::Dense(dense) => dense.contains(elem), + } + } + + pub fn superset(&self, other: &HybridBitSet<T>) -> bool { + match (self, other) { + (HybridBitSet::Dense(self_dense), HybridBitSet::Dense(other_dense)) => { + self_dense.superset(other_dense) + } + _ => { + assert!(self.domain_size() == other.domain_size()); + other.iter().all(|elem| self.contains(elem)) + } + } + } + + pub fn is_empty(&self) -> bool { + match self { + HybridBitSet::Sparse(sparse) => sparse.is_empty(), + HybridBitSet::Dense(dense) => dense.is_empty(), + } + } + + pub fn insert(&mut self, elem: T) -> bool { + // No need to check `elem` against `self.domain_size` here because all + // the match cases check it, one way or another. + match self { + HybridBitSet::Sparse(sparse) if sparse.len() < SPARSE_MAX => { + // The set is sparse and has space for `elem`. + sparse.insert(elem) + } + HybridBitSet::Sparse(sparse) if sparse.contains(elem) => { + // The set is sparse and does not have space for `elem`, but + // that doesn't matter because `elem` is already present. + false + } + HybridBitSet::Sparse(sparse) => { + // The set is sparse and full. Convert to a dense set. + let mut dense = sparse.to_dense(); + let changed = dense.insert(elem); + assert!(changed); + *self = HybridBitSet::Dense(dense); + changed + } + HybridBitSet::Dense(dense) => dense.insert(elem), + } + } + + pub fn insert_all(&mut self) { + let domain_size = self.domain_size(); + match self { + HybridBitSet::Sparse(_) => { + *self = HybridBitSet::Dense(BitSet::new_filled(domain_size)); + } + HybridBitSet::Dense(dense) => dense.insert_all(), + } + } + + pub fn remove(&mut self, elem: T) -> bool { + // Note: we currently don't bother going from Dense back to Sparse. + match self { + HybridBitSet::Sparse(sparse) => sparse.remove(elem), + HybridBitSet::Dense(dense) => dense.remove(elem), + } + } + + pub fn union(&mut self, other: &HybridBitSet<T>) -> bool { + match self { + HybridBitSet::Sparse(self_sparse) => { + match other { + HybridBitSet::Sparse(other_sparse) => { + // Both sets are sparse. Add the elements in + // `other_sparse` to `self` one at a time. This + // may or may not cause `self` to be densified. + assert_eq!(self.domain_size(), other.domain_size()); + let mut changed = false; + for elem in other_sparse.iter() { + changed |= self.insert(*elem); + } + changed + } + HybridBitSet::Dense(other_dense) => { + // `self` is sparse and `other` is dense. To + // merge them, we have two available strategies: + // * Densify `self` then merge other + // * Clone other then integrate bits from `self` + // The second strategy requires dedicated method + // since the usual `union` returns the wrong + // result. In the dedicated case the computation + // is slightly faster if the bits of the sparse + // bitset map to only few words of the dense + // representation, i.e. indices are near each + // other. + // + // Benchmarking seems to suggest that the second + // option is worth it. + let mut new_dense = other_dense.clone(); + let changed = new_dense.reverse_union_sparse(self_sparse); + *self = HybridBitSet::Dense(new_dense); + changed + } + } + } + + HybridBitSet::Dense(self_dense) => self_dense.union(other), + } + } + + /// Converts to a dense set, consuming itself in the process. + pub fn to_dense(self) -> BitSet<T> { + match self { + HybridBitSet::Sparse(sparse) => sparse.to_dense(), + HybridBitSet::Dense(dense) => dense, + } + } + + pub fn iter(&self) -> HybridIter<'_, T> { + match self { + HybridBitSet::Sparse(sparse) => HybridIter::Sparse(sparse.iter()), + HybridBitSet::Dense(dense) => HybridIter::Dense(dense.iter()), + } + } +} + +impl<T: Idx> UnionIntoBitSet<T> for HybridBitSet<T> { + fn union_into(&self, other: &mut BitSet<T>) -> bool { + match self { + HybridBitSet::Sparse(sparse) => sparse.union_into(other), + HybridBitSet::Dense(dense) => dense.union_into(other), + } + } +} + +impl<T: Idx> SubtractFromBitSet<T> for HybridBitSet<T> { + fn subtract_from(&self, other: &mut BitSet<T>) -> bool { + match self { + HybridBitSet::Sparse(sparse) => sparse.subtract_from(other), + HybridBitSet::Dense(dense) => dense.subtract_from(other), + } + } +} + +pub enum HybridIter<'a, T: Idx> { + Sparse(slice::Iter<'a, T>), + Dense(BitIter<'a, T>), +} + +impl<'a, T: Idx> Iterator for HybridIter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option<T> { + match self { + HybridIter::Sparse(sparse) => sparse.next().copied(), + HybridIter::Dense(dense) => dense.next(), + } + } +} + +/// A resizable bitset type with a dense representation. +/// +/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also +/// just be `usize`. +/// +/// All operations that involve an element will panic if the element is equal +/// to or greater than the domain size. +#[derive(Clone, Debug, PartialEq)] +pub struct GrowableBitSet<T: Idx> { + bit_set: BitSet<T>, +} + +impl<T: Idx> GrowableBitSet<T> { + /// Ensure that the set can hold at least `min_domain_size` elements. + pub fn ensure(&mut self, min_domain_size: usize) { + if self.bit_set.domain_size < min_domain_size { + self.bit_set.domain_size = min_domain_size; + } + + let min_num_words = num_words(min_domain_size); + if self.bit_set.words.len() < min_num_words { + self.bit_set.words.resize(min_num_words, 0) + } + } + + pub fn new_empty() -> GrowableBitSet<T> { + GrowableBitSet { bit_set: BitSet::new_empty(0) } + } + + pub fn with_capacity(capacity: usize) -> GrowableBitSet<T> { + GrowableBitSet { bit_set: BitSet::new_empty(capacity) } + } + + /// Returns `true` if the set has changed. + #[inline] + pub fn insert(&mut self, elem: T) -> bool { + self.ensure(elem.index() + 1); + self.bit_set.insert(elem) + } + + #[inline] + pub fn contains(&self, elem: T) -> bool { + let (word_index, mask) = word_index_and_mask(elem); + if let Some(word) = self.bit_set.words.get(word_index) { + (word & mask) != 0 + } else { + false + } + } +} + +/// A fixed-size 2D bit matrix type with a dense representation. +/// +/// `R` and `C` are index types used to identify rows and columns respectively; +/// typically newtyped `usize` wrappers, but they can also just be `usize`. +/// +/// All operations that involve a row and/or column index will panic if the +/// index exceeds the relevant bound. +#[derive(Clone, Debug, Eq, PartialEq, RustcDecodable, RustcEncodable)] +pub struct BitMatrix<R: Idx, C: Idx> { + num_rows: usize, + num_columns: usize, + words: Vec<Word>, + marker: PhantomData<(R, C)>, +} + +impl<R: Idx, C: Idx> BitMatrix<R, C> { + /// Creates a new `rows x columns` matrix, initially empty. + pub fn new(num_rows: usize, num_columns: usize) -> BitMatrix<R, C> { + // For every element, we need one bit for every other + // element. Round up to an even number of words. + let words_per_row = num_words(num_columns); + BitMatrix { + num_rows, + num_columns, + words: vec![0; num_rows * words_per_row], + marker: PhantomData, + } + } + + /// Creates a new matrix, with `row` used as the value for every row. + pub fn from_row_n(row: &BitSet<C>, num_rows: usize) -> BitMatrix<R, C> { + let num_columns = row.domain_size(); + let words_per_row = num_words(num_columns); + assert_eq!(words_per_row, row.words().len()); + BitMatrix { + num_rows, + num_columns, + words: iter::repeat(row.words()).take(num_rows).flatten().cloned().collect(), + marker: PhantomData, + } + } + + pub fn rows(&self) -> impl Iterator<Item = R> { + (0..self.num_rows).map(R::new) + } + + /// The range of bits for a given row. + fn range(&self, row: R) -> (usize, usize) { + let words_per_row = num_words(self.num_columns); + let start = row.index() * words_per_row; + (start, start + words_per_row) + } + + /// Sets the cell at `(row, column)` to true. Put another way, insert + /// `column` to the bitset for `row`. + /// + /// Returns `true` if this changed the matrix. + pub fn insert(&mut self, row: R, column: C) -> bool { + assert!(row.index() < self.num_rows && column.index() < self.num_columns); + let (start, _) = self.range(row); + let (word_index, mask) = word_index_and_mask(column); + let words = &mut self.words[..]; + let word = words[start + word_index]; + let new_word = word | mask; + words[start + word_index] = new_word; + word != new_word + } + + /// Do the bits from `row` contain `column`? Put another way, is + /// the matrix cell at `(row, column)` true? Put yet another way, + /// if the matrix represents (transitive) reachability, can + /// `row` reach `column`? + pub fn contains(&self, row: R, column: C) -> bool { + assert!(row.index() < self.num_rows && column.index() < self.num_columns); + let (start, _) = self.range(row); + let (word_index, mask) = word_index_and_mask(column); + (self.words[start + word_index] & mask) != 0 + } + + /// Returns those indices that are true in rows `a` and `b`. This + /// is an O(n) operation where `n` is the number of elements + /// (somewhat independent from the actual size of the + /// intersection, in particular). + pub fn intersect_rows(&self, row1: R, row2: R) -> Vec<C> { + assert!(row1.index() < self.num_rows && row2.index() < self.num_rows); + let (row1_start, row1_end) = self.range(row1); + let (row2_start, row2_end) = self.range(row2); + let mut result = Vec::with_capacity(self.num_columns); + for (base, (i, j)) in (row1_start..row1_end).zip(row2_start..row2_end).enumerate() { + let mut v = self.words[i] & self.words[j]; + for bit in 0..WORD_BITS { + if v == 0 { + break; + } + if v & 0x1 != 0 { + result.push(C::new(base * WORD_BITS + bit)); + } + v >>= 1; + } + } + result + } + + /// Adds the bits from row `read` to the bits from row `write`, and + /// returns `true` if anything changed. + /// + /// This is used when computing transitive reachability because if + /// you have an edge `write -> read`, because in that case + /// `write` can reach everything that `read` can (and + /// potentially more). + pub fn union_rows(&mut self, read: R, write: R) -> bool { + assert!(read.index() < self.num_rows && write.index() < self.num_rows); + let (read_start, read_end) = self.range(read); + let (write_start, write_end) = self.range(write); + let words = &mut self.words[..]; + let mut changed = false; + for (read_index, write_index) in (read_start..read_end).zip(write_start..write_end) { + let word = words[write_index]; + let new_word = word | words[read_index]; + words[write_index] = new_word; + changed |= word != new_word; + } + changed + } + + /// Adds the bits from `with` to the bits from row `write`, and + /// returns `true` if anything changed. + pub fn union_row_with(&mut self, with: &BitSet<C>, write: R) -> bool { + assert!(write.index() < self.num_rows); + assert_eq!(with.domain_size(), self.num_columns); + let (write_start, write_end) = self.range(write); + let mut changed = false; + for (read_index, write_index) in (0..with.words().len()).zip(write_start..write_end) { + let word = self.words[write_index]; + let new_word = word | with.words()[read_index]; + self.words[write_index] = new_word; + changed |= word != new_word; + } + changed + } + + /// Sets every cell in `row` to true. + pub fn insert_all_into_row(&mut self, row: R) { + assert!(row.index() < self.num_rows); + let (start, end) = self.range(row); + let words = &mut self.words[..]; + for index in start..end { + words[index] = !0; + } + self.clear_excess_bits(row); + } + + /// Clear excess bits in the final word of the row. + fn clear_excess_bits(&mut self, row: R) { + let num_bits_in_final_word = self.num_columns % WORD_BITS; + if num_bits_in_final_word > 0 { + let mask = (1 << num_bits_in_final_word) - 1; + let (_, end) = self.range(row); + let final_word_idx = end - 1; + self.words[final_word_idx] &= mask; + } + } + + /// Gets a slice of the underlying words. + pub fn words(&self) -> &[Word] { + &self.words + } + + /// Iterates through all the columns set to true in a given row of + /// the matrix. + pub fn iter(&self, row: R) -> BitIter<'_, C> { + assert!(row.index() < self.num_rows); + let (start, end) = self.range(row); + BitIter::new(&self.words[start..end]) + } + + /// Returns the number of elements in `row`. + pub fn count(&self, row: R) -> usize { + let (start, end) = self.range(row); + self.words[start..end].iter().map(|e| e.count_ones() as usize).sum() + } +} + +/// A fixed-column-size, variable-row-size 2D bit matrix with a moderately +/// sparse representation. +/// +/// Initially, every row has no explicit representation. If any bit within a +/// row is set, the entire row is instantiated as `Some(<HybridBitSet>)`. +/// Furthermore, any previously uninstantiated rows prior to it will be +/// instantiated as `None`. Those prior rows may themselves become fully +/// instantiated later on if any of their bits are set. +/// +/// `R` and `C` are index types used to identify rows and columns respectively; +/// typically newtyped `usize` wrappers, but they can also just be `usize`. +#[derive(Clone, Debug)] +pub struct SparseBitMatrix<R, C> +where + R: Idx, + C: Idx, +{ + num_columns: usize, + rows: IndexVec<R, Option<HybridBitSet<C>>>, +} + +impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { + /// Creates a new empty sparse bit matrix with no rows or columns. + pub fn new(num_columns: usize) -> Self { + Self { + num_columns, + rows: IndexVec::new(), + } + } + + fn ensure_row(&mut self, row: R) -> &mut HybridBitSet<C> { + // Instantiate any missing rows up to and including row `row` with an + // empty HybridBitSet. + self.rows.ensure_contains_elem(row, || None); + + // Then replace row `row` with a full HybridBitSet if necessary. + let num_columns = self.num_columns; + self.rows[row].get_or_insert_with(|| HybridBitSet::new_empty(num_columns)) + } + + /// Sets the cell at `(row, column)` to true. Put another way, insert + /// `column` to the bitset for `row`. + /// + /// Returns `true` if this changed the matrix. + pub fn insert(&mut self, row: R, column: C) -> bool { + self.ensure_row(row).insert(column) + } + + /// Do the bits from `row` contain `column`? Put another way, is + /// the matrix cell at `(row, column)` true? Put yet another way, + /// if the matrix represents (transitive) reachability, can + /// `row` reach `column`? + pub fn contains(&self, row: R, column: C) -> bool { + self.row(row).map_or(false, |r| r.contains(column)) + } + + /// Adds the bits from row `read` to the bits from row `write`, and + /// returns `true` if anything changed. + /// + /// This is used when computing transitive reachability because if + /// you have an edge `write -> read`, because in that case + /// `write` can reach everything that `read` can (and + /// potentially more). + pub fn union_rows(&mut self, read: R, write: R) -> bool { + if read == write || self.row(read).is_none() { + return false; + } + + self.ensure_row(write); + if let (Some(read_row), Some(write_row)) = self.rows.pick2_mut(read, write) { + write_row.union(read_row) + } else { + unreachable!() + } + } + + /// Union a row, `from`, into the `into` row. + pub fn union_into_row(&mut self, into: R, from: &HybridBitSet<C>) -> bool { + self.ensure_row(into).union(from) + } + + /// Insert all bits in the given row. + pub fn insert_all_into_row(&mut self, row: R) { + self.ensure_row(row).insert_all(); + } + + pub fn rows(&self) -> impl Iterator<Item = R> { + self.rows.indices() + } + + /// Iterates through all the columns set to true in a given row of + /// the matrix. + pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a { + self.row(row).into_iter().flat_map(|r| r.iter()) + } + + pub fn row(&self, row: R) -> Option<&HybridBitSet<C>> { + if let Some(Some(row)) = self.rows.get(row) { + Some(row) + } else { + None + } + } +} + +#[inline] +fn num_words<T: Idx>(domain_size: T) -> usize { + (domain_size.index() + WORD_BITS - 1) / WORD_BITS +} + +#[inline] +fn word_index_and_mask<T: Idx>(elem: T) -> (usize, Word) { + let elem = elem.index(); + let word_index = elem / WORD_BITS; + let mask = 1 << (elem % WORD_BITS); + (word_index, mask) +}
diff --git a/src/librustc_data_structures/bit_set/tests.rs b/src/librustc_index/bit_set/tests.rs similarity index 100% rename from src/librustc_data_structures/bit_set/tests.rs rename to src/librustc_index/bit_set/tests.rs
diff --git a/src/librustc_index/lib.rs b/src/librustc_index/lib.rs new file mode 100644 index 0000000..ad242df --- /dev/null +++ b/src/librustc_index/lib.rs
@@ -0,0 +1,7 @@ +#![feature(allow_internal_unstable)] +#![feature(unboxed_closures)] +#![feature(test)] +#![feature(fn_traits)] + +pub mod vec; +pub mod bit_set;
diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_index/vec.rs similarity index 100% rename from src/librustc_data_structures/indexed_vec.rs rename to src/librustc_index/vec.rs
diff --git a/src/librustc_interface/Cargo.toml b/src/librustc_interface/Cargo.toml index f629310..0d8d765 100644 --- a/src/librustc_interface/Cargo.toml +++ b/src/librustc_interface/Cargo.toml
@@ -11,14 +11,14 @@ [dependencies] log = "0.4" -rayon = { version = "0.2.0", package = "rustc-rayon" } +rayon = { version = "0.3.0", package = "rustc-rayon" } smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } syntax = { path = "../libsyntax" } syntax_ext = { path = "../libsyntax_ext" } +syntax_expand = { path = "../libsyntax_expand" } syntax_pos = { path = "../libsyntax_pos" } rustc_serialize = { path = "../libserialize", package = "serialize" } rustc = { path = "../librustc" } -rustc_ast_borrowck = { path = "../librustc_ast_borrowck" } rustc_incremental = { path = "../librustc_incremental" } rustc_traits = { path = "../librustc_traits" } rustc_data_structures = { path = "../librustc_data_structures" } @@ -27,6 +27,7 @@ rustc_metadata = { path = "../librustc_metadata" } rustc_mir = { path = "../librustc_mir" } rustc_passes = { path = "../librustc_passes" } +rustc_target = { path = "../librustc_target" } rustc_typeck = { path = "../librustc_typeck" } rustc_lint = { path = "../librustc_lint" } rustc_errors = { path = "../librustc_errors" }
diff --git a/src/librustc_interface/interface.rs b/src/librustc_interface/interface.rs index fef60a4..e014e4e 100644 --- a/src/librustc_interface/interface.rs +++ b/src/librustc_interface/interface.rs
@@ -1,23 +1,26 @@ use crate::queries::Queries; use crate::util; -use crate::profile; pub use crate::passes::BoxedResolver; use rustc::lint; -use rustc::session::config::{self, Input}; +use rustc::session::early_error; +use rustc::session::config::{self, Input, ErrorOutputType}; use rustc::session::{DiagnosticOutput, Session}; use rustc::util::common::ErrorReported; use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_data_structures::OnDrop; use rustc_data_structures::sync::Lrc; use rustc_data_structures::fx::{FxHashSet, FxHashMap}; -use rustc_metadata::cstore::CStore; use std::path::PathBuf; use std::result; use std::sync::{Arc, Mutex}; -use syntax; -use syntax::source_map::{FileLoader, SourceMap}; +use syntax::{self, parse}; +use syntax::ast::{self, MetaItemKind}; +use syntax::parse::token; +use syntax::source_map::{FileName, FilePathMapping, FileLoader, SourceMap}; +use syntax::sess::ParseSess; use syntax_pos::edition; +use rustc_errors::{Diagnostic, emitter::Emitter, Handler, SourceMapperDyn}; pub type Result<T> = result::Result<T, ErrorReported>; @@ -33,8 +36,8 @@ pub(crate) output_dir: Option<PathBuf>, pub(crate) output_file: Option<PathBuf>, pub(crate) queries: Queries, - pub(crate) cstore: Lrc<CStore>, pub(crate) crate_name: Option<String>, + pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut lint::LintStore) + Send + Sync>>, } impl Compiler { @@ -44,9 +47,6 @@ pub fn codegen_backend(&self) -> &Lrc<Box<dyn CodegenBackend>> { &self.codegen_backend } - pub fn cstore(&self) -> &Lrc<CStore> { - &self.cstore - } pub fn source_map(&self) -> &Lrc<SourceMap> { &self.source_map } @@ -61,6 +61,58 @@ } } +/// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`. +pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> { + struct NullEmitter; + impl Emitter for NullEmitter { + fn emit_diagnostic(&mut self, _: &Diagnostic) {} + fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> { None } + } + + syntax::with_default_globals(move || { + let cfg = cfgspecs.into_iter().map(|s| { + + let cm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let handler = Handler::with_emitter(false, None, Box::new(NullEmitter)); + let sess = ParseSess::with_span_handler(handler, cm); + let filename = FileName::cfg_spec_source_code(&s); + let mut parser = parse::new_parser_from_source_str(&sess, filename, s.to_string()); + + macro_rules! error {($reason: expr) => { + early_error(ErrorOutputType::default(), + &format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s)); + }} + + match &mut parser.parse_meta_item() { + Ok(meta_item) if parser.token == token::Eof => { + if meta_item.path.segments.len() != 1 { + error!("argument key must be an identifier"); + } + match &meta_item.kind { + MetaItemKind::List(..) => { + error!(r#"expected `key` or `key="value"`"#); + } + MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { + error!("argument value must be a string"); + } + MetaItemKind::NameValue(..) | MetaItemKind::Word => { + let ident = meta_item.ident().expect("multi-segment cfg key"); + return (ident.name, meta_item.value_str()); + } + } + } + Ok(..) => {} + Err(err) => err.cancel(), + } + + error!(r#"expected `key` or `key="value"`"#); + }).collect::<ast::CrateConfig>(); + cfg.into_iter().map(|(a, b)| { + (a.to_string(), b.map(|b| b.to_string())) + }).collect() + }) +} + /// The compiler configuration pub struct Config { /// Command line options @@ -81,6 +133,13 @@ pub crate_name: Option<String>, pub lint_caps: FxHashMap<lint::LintId, lint::Level>, + + /// This is a callback from the driver that is called when we're registering lints; + /// it is called during plugin registration when we have the LintStore in a non-shared state. + /// + /// Note that if you find a Some here you probably want to call that function in the new + /// function being registered. + pub register_lints: Option<Box<dyn Fn(&Session, &mut lint::LintStore) + Send + Sync>>, } pub fn run_compiler_in_existing_thread_pool<F, R>(config: Config, f: F) -> R @@ -96,36 +155,24 @@ config.lint_caps, ); - let cstore = Lrc::new(CStore::new(codegen_backend.metadata_loader())); - let compiler = Compiler { sess, codegen_backend, source_map, - cstore, input: config.input, input_path: config.input_path, output_dir: config.output_dir, output_file: config.output_file, queries: Default::default(), crate_name: config.crate_name, + register_lints: config.register_lints, }; let _sess_abort_error = OnDrop(|| { compiler.sess.diagnostic().print_error_count(&util::diagnostics_registry()); }); - if compiler.sess.profile_queries() { - profile::begin(&compiler.sess); - } - - let r = f(&compiler); - - if compiler.sess.profile_queries() { - profile::dump(&compiler.sess, "profile_queries".to_string()) - } - - r + f(&compiler) } pub fn run_compiler<F, R>(mut config: Config, f: F) -> R @@ -147,5 +194,8 @@ F: FnOnce() -> R + Send, R: Send, { - util::spawn_thread_pool(edition, None, &None, f) + // the 1 here is duplicating code in config.opts.debugging_opts.threads + // which also defaults to 1; it ultimately doesn't matter as the default + // isn't threaded, and just ignores this parameter + util::spawn_thread_pool(edition, 1, &None, f) }
diff --git a/src/librustc_interface/lib.rs b/src/librustc_interface/lib.rs index 2e593d4..53baf65 100644 --- a/src/librustc_interface/lib.rs +++ b/src/librustc_interface/lib.rs
@@ -16,6 +16,8 @@ mod queries; pub mod util; mod proc_macro_decls; -mod profile; pub use interface::{run_compiler, Config}; + +#[cfg(test)] +mod tests;
diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index e8e8da6..5233274 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs
@@ -9,23 +9,21 @@ use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc::lint; use rustc::middle::{self, reachable, resolve_lifetime, stability}; -use rustc::middle::cstore::CrateStore; -use rustc::ty::{self, AllArenas, Resolutions, TyCtxt, GlobalCtxt}; +use rustc::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn}; +use rustc::ty::{self, AllArenas, ResolverOutputs, TyCtxt, GlobalCtxt}; use rustc::ty::steal::Steal; use rustc::traits; use rustc::util::common::{time, ErrorReported}; use rustc::session::Session; use rustc::session::config::{self, CrateType, Input, OutputFilenames, OutputType}; use rustc::session::search_paths::PathKind; -use rustc_ast_borrowck as borrowck; use rustc_codegen_ssa::back::link::emit_metadata; use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_codegen_utils::link::filename_for_metadata; use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel}; use rustc_data_structures::sync::{Lrc, ParallelIterator, par_iter}; use rustc_incremental; -use rustc_metadata::creader::CrateLoader; -use rustc_metadata::cstore::{self, CStore}; +use rustc_metadata::cstore; use rustc_mir as mir; use rustc_passes::{self, ast_validation, hir_stats, layout_test}; use rustc_plugin as plugin; @@ -36,7 +34,7 @@ use rustc_typeck as typeck; use syntax::{self, ast, visit}; use syntax::early_buffered_lints::BufferedEarlyLint; -use syntax::ext::base::{NamedSyntaxExtension, ExtCtxt}; +use syntax_expand::base::{NamedSyntaxExtension, ExtCtxt}; use syntax::mut_visit::MutVisitor; use syntax::parse::{self, PResult}; use syntax::util::node_count::NodeCounter; @@ -47,29 +45,28 @@ use rustc_serialize::json; use tempfile::Builder as TempFileBuilder; +use std::{env, fs, iter, mem}; use std::any::Any; -use std::env; use std::ffi::OsString; -use std::fs; use std::io::{self, Write}; -use std::iter; use std::path::PathBuf; -use std::sync::mpsc; use std::cell::RefCell; use std::rc::Rc; pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> { sess.diagnostic() .set_continue_after_error(sess.opts.debugging_opts.continue_parse_after_error); - sess.profiler(|p| p.start_activity("parsing")); - let krate = time(sess, "parsing", || match *input { - Input::File(ref file) => parse::parse_crate_from_file(file, &sess.parse_sess), - Input::Str { - ref input, - ref name, - } => parse::parse_crate_from_source_str(name.clone(), input.clone(), &sess.parse_sess), + let krate = time(sess, "parsing", || { + let _prof_timer = sess.prof.generic_activity("parse_crate"); + + match *input { + Input::File(ref file) => parse::parse_crate_from_file(file, &sess.parse_sess), + Input::Str { + ref input, + ref name, + } => parse::parse_crate_from_source_str(name.clone(), input.clone(), &sess.parse_sess), + } })?; - sess.profiler(|p| p.end_activity("parsing")); sess.diagnostic().set_continue_after_error(true); @@ -105,7 +102,7 @@ declare_box_region_type!( pub BoxedResolver, for(), - (&mut Resolver<'_>) -> (Result<ast::Crate>, ExpansionResult) + (&mut Resolver<'_>) -> (Result<ast::Crate>, ResolverOutputs) ); /// Runs the "early phases" of the compiler: initial `cfg` processing, @@ -117,7 +114,8 @@ /// Returns `None` if we're aborting after handling -W help. pub fn configure_and_expand( sess: Lrc<Session>, - cstore: Lrc<CStore>, + lint_store: Lrc<lint::LintStore>, + metadata_loader: Box<MetadataLoaderDyn>, krate: ast::Crate, crate_name: &str, plugin_info: PluginInfo, @@ -130,15 +128,14 @@ let crate_name = crate_name.to_string(); let (result, resolver) = BoxedResolver::new(static move || { let sess = &*sess; - let mut crate_loader = CrateLoader::new(sess, &*cstore, &crate_name); let resolver_arenas = Resolver::arenas(); let res = configure_and_expand_inner( sess, - &*cstore, + &lint_store, krate, &crate_name, &resolver_arenas, - &mut crate_loader, + &*metadata_loader, plugin_info, ); let mut resolver = match res { @@ -152,66 +149,16 @@ } }; box_region_allow_access!(for(), (&mut Resolver<'_>), (&mut resolver)); - ExpansionResult::from_owned_resolver(resolver) + resolver.into_outputs() }); result.map(|k| (k, resolver)) } -pub struct ExpansionResult { - pub defs: Steal<hir::map::Definitions>, - pub resolutions: Steal<Resolutions>, -} - -impl ExpansionResult { - fn from_owned_resolver( - resolver: Resolver<'_>, - ) -> Self { - ExpansionResult { - defs: Steal::new(resolver.definitions), - resolutions: Steal::new(Resolutions { - export_map: resolver.export_map, - trait_map: resolver.trait_map, - glob_map: resolver.glob_map, - maybe_unused_trait_imports: resolver.maybe_unused_trait_imports, - maybe_unused_extern_crates: resolver.maybe_unused_extern_crates, - extern_prelude: resolver.extern_prelude.iter().map(|(ident, entry)| { - (ident.name, entry.introduced_by_item) - }).collect(), - }), - } - } - - pub fn from_resolver_ref( - resolver: &Resolver<'_>, - ) -> Self { - ExpansionResult { - defs: Steal::new(resolver.definitions.clone()), - resolutions: Steal::new(Resolutions { - export_map: resolver.export_map.clone(), - trait_map: resolver.trait_map.clone(), - glob_map: resolver.glob_map.clone(), - maybe_unused_trait_imports: resolver.maybe_unused_trait_imports.clone(), - maybe_unused_extern_crates: resolver.maybe_unused_extern_crates.clone(), - extern_prelude: resolver.extern_prelude.iter().map(|(ident, entry)| { - (ident.name, entry.introduced_by_item) - }).collect(), - }), - } - } -} - impl BoxedResolver { - pub fn to_expansion_result( - resolver: Rc<RefCell<BoxedResolver>>, - ) -> ExpansionResult { + pub fn to_resolver_outputs(resolver: Rc<RefCell<BoxedResolver>>) -> ResolverOutputs { match Rc::try_unwrap(resolver) { Ok(resolver) => resolver.into_inner().complete(), - Err(resolver) => { - let resolver = &*resolver; - resolver.borrow_mut().access(|resolver| { - ExpansionResult::from_resolver_ref(resolver) - }) - } + Err(resolver) => resolver.borrow_mut().access(|resolver| resolver.clone_outputs()), } } } @@ -221,12 +168,12 @@ } pub fn register_plugins<'a>( - compiler: &Compiler, sess: &'a Session, - cstore: &'a CStore, + metadata_loader: &'a dyn MetadataLoader, + register_lints: impl Fn(&Session, &mut lint::LintStore), mut krate: ast::Crate, crate_name: &str, -) -> Result<(ast::Crate, PluginInfo)> { +) -> Result<(ast::Crate, PluginInfo, Lrc<lint::LintStore>)> { krate = time(sess, "attributes injection", || { syntax_ext::cmdline_attrs::inject( krate, &sess.parse_sess, &sess.opts.debugging_opts.crate_attr @@ -250,7 +197,9 @@ rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator); if sess.opts.incremental.is_some() { - time(sess, "garbage collect incremental cache directory", || { + time(sess, "garbage-collect incremental cache directory", || { + let _prof_timer = + sess.prof.generic_activity("incr_comp_garbage_collect_session_directories"); if let Err(e) = rustc_incremental::garbage_collect_session_directories(sess) { warn!( "Error while trying to garbage collect incremental \ @@ -261,9 +210,6 @@ }); } - // If necessary, compute the dependency graph (in the background). - compiler.dep_graph_future().ok(); - time(sess, "recursion limit", || { middle::recursion_limit::update_limits(sess, &krate); }); @@ -271,14 +217,20 @@ let registrars = time(sess, "plugin loading", || { plugin::load::load_plugins( sess, - &cstore, + metadata_loader, &krate, - crate_name, Some(sess.opts.debugging_opts.extra_plugins.clone()), ) }); - let mut registry = Registry::new(sess, krate.span); + let mut lint_store = rustc_lint::new_lint_store( + sess.opts.debugging_opts.no_interleave_lints, + sess.unstable_options(), + ); + + (register_lints)(&sess, &mut lint_store); + + let mut registry = Registry::new(sess, &mut lint_store, krate.span); time(sess, "plugin registration", || { for registrar in registrars { @@ -289,55 +241,41 @@ let Registry { syntax_exts, - early_lint_passes, - late_lint_passes, - lint_groups,